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

HTTP/2 即将发布

$
0
0

近日,互联网工程任务组(Internet Engineering Task Force ,IETF)对HTTP/2做了最后修整,并准备正式发布HTTP/2。HTTP/2是新一代的HTTP协议,它是由IETF的httpbis工作组开发,基于SPDY协议,目标是在保持与HTTP 1.1语义向后兼容性的情况下,减小网络传输延迟,并简化服务器向浏览器传输内容的过程。

目前,工作组已经发布了 最终草案,并广泛征求社区建议。开源的Servlet容器软件Jetty的开发负责人 Greg Wilkins在其博客中谈了他对 HTTP/2的看法,他认为HTTP/2有很多值得称赞的地方,比如保持了HTTP/1.1的语义、支持服务器推送。但它也有很多糟糕的地方,比如在 WebSocket与HTTP/2.0草案中提到的很多新特性都未得到支持。此外,Nginx的产品负责人Owen Garrett称他们的很多用户都对此协议进行了测试使用,普遍都认为性能得到了较大提升。

第一代的Web站点都相对比较简单并且静态资源也相对比较少,而现在的Web动辄就是几兆的图片而且对实时性也要求比较高。为了适应新的需求,HTTP/2通过多种方式来加速HTTP。比如HTTP/2允许服务器一次发送所请求的Web页面的所有元素,以减少HTTP的负荷。

另外,HTTP/2对于那些拥有复杂的Web站点的组织更有用,特别是当其用户分布在全球或者用户网络不好的情况,比如手机用户。关于HTTP/2的更多信息读者可以参考 此文档

via InfoQ


著名的麦肯锡信任公式:如何取得别人的信任?

$
0
0

著名的麦肯锡信任公式:信任=可靠性×资质能力×亲近程度/自我取向。拼搏职场的同学,建议好好琢磨。

著名的麦肯锡信任公式:如何取得别人的信任?| jiaren.org

如何取得别人的信任?

这个题目乍一看太过功利,会被一部分有原则的人,痛斥为沉迷于各种墙头草两边倒技术的左右逢源主义信奉者,可是我要问那些所谓有原则的人,如何坚持自己的原则,要分步骤或是分要点吗?坚持原则也要有抓手有技术的对吧,所以看这篇文章以及相信其本身带给你的东西,不影响你去做一个堂堂正正的有原则的人。

开门见山的讲,我认为的信任可以用下边的公式来表示,即 信任=可靠性×资质能力×亲近程度/自我取向,总共包含了4个要素,一个一个的讲。

可靠性

简单的讲就是你做事情的靠谱程度,和事情的大小复杂程度无关,最为简单的就是,如果让你拿公司门的钥匙,你是否能坚持每天第一个到公司准时准点开门,不出现迟到或是忘带钥匙等低级错误,或许一天可以,如果是一年十年或是更长时间呢?如果你能将一件事情持续不断的做好,就是一种可靠性或是靠谱的表现,也就是张瑞敏所说的,把简单的事情做好就是不简单,平凡的事情做好就是不平凡。

资质能力

可以分为两个部分看待,一部分是资质,也就是你的经验和头衔的总称,如果你有十年的管理经验,加上一张沧桑的脸和稳重的肚腩,以及一个企业GM的名片,也就是一个典型的中年成熟稳重男人的形象,别人就很容易对你产生信任感。当然这只能说是一个前提吧,如果你一张嘴说出的都是不靠谱的话,办的都是蠢事,外在这套东西瞬间都会成为浮云,所以自然而然就联系到了第二部分,能力。

能力也可以大体分为两类,一类是专业能力,一类是可以理解为比较普遍的工作能力和方法。专业能力容易理解为你所专业擅长的领域,术业有专攻,李逵善于拿斧头砍人,诸葛亮善于运筹帷幄,李师师善于房中术,等等,至于朱元璋放弃自己和尚的专业当起了皇帝,这就另当别论了。

第二类工作能力和工作方法,可以理解为作为一个职场中人的应该具有的职业操守,诸如被说烂了的执行力理论,诸如穿衣谈吐,诸如从计划到执行到改进的PDCA方法,等等都是我们做工作的最基本的方法,这些可以凝结在日常工作中去反映出你的职业操守。

亲近程度

亲近程度最为简单,就是你和你要取得信任的对象亲近程度。如果你对某人总是敬而远之,你的能力可靠性再强也无济于事,要取得别人的信任,就要能和其称兄道弟,一个鼻孔出气。但是警告一点的就是,职场中永远没有真正的朋友,不要把亲近当做是真亲近,你和别人掏心窝子,只能让别人觉得你不专业。

自我取向

最后一项就是自我取向,这个是一个分母,与信任程度呈反比,说白了别太把自己当回事。眼里有客户,心里有别人,在和别人面对面坐着的时候,要好好想想别人的想法是什么,别人有什么需要,眼前的这个人是不是累了是不是口渴需要休息等等。如果能考虑到这种程度,那么你自然就达到心中无我了,别人就会发现和你相处是一件很愉快的事情。

以上就是如何取得别人信任的方法,四个要素,各有各的含义,各有各的特点,细细揣摩,慢慢实践,终有一天你会悟道。( 来源

著名的麦肯锡信任公式:如何取得别人的信任?| jiaren.org小提示:如果文章有分页可能会被截断,请 点此阅读全文

佳人专注精品阅读6年,多谢佳友们的支持,请记得在QQ邮件底部评分处打5颗星噢~~


著名的麦肯锡信任公式:如何取得别人的信任?| jiaren.org把佳人藏进QQ邮箱 | 人气热文 | 投稿 | 关注佳人QQ空间 | 小组 | 佳人首页 | 著名的麦肯锡信任公式:如何取得别人的信任?| jiaren.org佳人微信

佳人推荐:

佳人猜您也喜欢:
著名的麦肯锡信任公式:如何取得别人的信任?| jiaren.org
马云卸任阿里CEO演讲:因为信任,所以简单
著名的麦肯锡信任公式:如何取得别人的信任?| jiaren.org
我们很少信任比我们好的人
著名的麦肯锡信任公式:如何取得别人的信任?| jiaren.org
梁文道:犬儒时代的信任
著名的麦肯锡信任公式:如何取得别人的信任?| jiaren.org
有时送出信任,会让你觉得自己是个好人
著名的麦肯锡信任公式:如何取得别人的信任?| jiaren.org
第一次结婚,请多关照
无觅

MySQL InnoDB存储引擎参数详解及优化

$
0
0
innodb_data_home_dir这是InnoDB表的目录共用设置。如果没有在 my.cnf 进行设置,InnoDB 将使用mysq l的datadir目录为缺省目录。如果设定一个空字串,可以innodb_data_file_path中设定绝对路径。
innodb_data_file_path单独指定数据文件的路径与大小。数据文件的完整路径由 innodb_data_home_dir 与这里所设定值的组合。 文件大小以 MB 单位指定。因此在文件大小指定后必有“M”。 InnoDB 也支持缩写“G”, 1G = 1024M。从 3.23.44 开始,在那些支持大文件的操作系统上可以设置数据文件大小大于 4 GB。而在另一些操作系统上数据文件必须小于 2 GB。数据文件大小总和至少要达到 10 MB。在 MySQL-3.23 中这个参数必须在 my.cnf 中明确指定。在 MySQL-4.0.2 以及更新版本中则不需如此,系统会默认在 MySQL 的datadir目录下创建一个 16 MB 自扩充(auto-extending)的数据文件ibdata1。你同样可以使用一个 原生磁盘分区(RAW raw disk partitions(raw devices)) 作为数据文件
innodb_mirrored_log_groups为了保护数据而设置的日志文件组的拷贝数目,默认设置为 1。在my.cnf 中以数字格式设置。
innodb_log_group_home_dirInnoDB 日志文件的路径。必须与innodb_log_arch_dir设置相同值。 如果没有明确指定将默认在 MySQL 的datadir目录下建立两个 5 MB 大小的 ib_logfile… 文件。
innodb_log_files_in_group日志组中的日志文件数目。InnoDB 以环型方式(circular fashion)写入文件。数值 3 被推荐使用。在 my.cnf 中以数字格式设置。
innodb_log_file_size日志组中的每个日志文件的大小(单位 MB)。如果 n 是日志组中日志文件的数目,那么理想的数值为 1M 至下面设置的缓冲池(buffer pool)大小的 1/n。较大的值,可以减少刷新缓冲池的次数,从而减少磁盘 I/O。但是大的日志文件意味着在崩溃时需要更长的时间来恢复数据。 日志文件总和必须小于 2 GB,3.23.55 和 4.0.9 以上为小于 4 GB。在my.cnf 中以数字格式设置。
innodb_log_buffer_sizeInnoDB 将日志写入日志磁盘文件前的缓冲大小。理想值为 1M 至 8M。大的日志缓冲允许事务运行时不需要将日志保存入磁盘而只到事务被提交(commit)。 因此,如果有大的事务处理,设置大的日志缓冲可以减少磁盘I/O。 在 my.cnf 中以数字格式设置。
innodb_flush_log_at_trx_commit通常设置为 1,意味着在事务提交前日志已被写入磁盘, 事务可以运行更长以及服务崩溃后的修复能力。如果你愿意减弱这个安全,或你运行的是比较小的事务处理,可以将它设置为 0 ,以减少写日志文件的磁盘 I/O。这个选项默认设置为 0。
innodb_log_arch_dirThe directory where fully written log files would be archived if we used log archiving. 这里设置的参数必须与innodb_log_group_home_dir相同。 从 4.0.6 开始,可以忽略这个参数。
innodb_log_archive这个值通常设为 0。 既然从备份中恢复(recovery)适合于 MySQL 使用它自己的 log files,因而通常不再需要 archive InnoDB log files。这个选项默认设置为 0。
innodb_buffer_pool_sizeInnoDB 用来高速缓冲数据和索引内存缓冲大小。 更大的设置可以使访问数据时减少磁盘 I/O。在一个专用的数据库服务器上可以将它设置为物理内存的 80 %。 不要将它设置太大,因为物理内存的使用竞争可能会影响操作系统的页面调用。在 my.cnf 中以数字格式设置。
innodb_additional_mem_pool_sizeInnoDB 用来存储数据字典(data dictionary)信息和其它内部数据结构(internal data structures)的存储器组合(memory pool)大小。理想的值为 2M,如果有更多的表你就需要在这里重新分配。如果 InnoDB 用尽这个池中的所有内存,它将从操作系统中分配内存,并将错误信息写入 MySQL 的错误日志中。在 my.cnf 中以数字格式设置。
innodb_file_io_threadsInnoDB 中的文件 I/O 线程。 通常设置为 4,但是在 windows 下可以设定一个更大的值以提高磁盘 I/O。在 my.cnf 中以数字格式设置。
innodb_lock_wait_timeout在回滚(rooled back)之前,InnoDB 事务将等待超时的时间(单位 秒)。InnoDB 会自动检查自身在锁定表与事务回滚时的事务死锁。如果使用LOCK TABLES命 令,或在同一个事务中使用其它事务安全型表处理器(transaction safe table handlers than InnoDB),那么可能会发生一个 InnoDB 无法注意到的死锁。在这种情况下超时将用来解决这个问题。这个参数的默认值为 50 秒。在 my.cnf 中以数字格式设置。
innodb_flush_method这个参数仅仅与 Unix 相关。这个参数默认值为fdatasync。 另一个设置项为O_DSYNC。这仅仅影响日志文件的转储,在 Unix 下以fsync转储数据。InnoDB 版本从 3.23.40b 开始,在 Unix 下指定fdatasync 为使用fsync方式、指定O_DSYNC为使用O_SYNC方式。由于这在某些 Unix 环境下还有些问题所以在 ‘data’ versions 并没有被使用。
innodb_force_recovery警告:此参数只能在你希望从一个被损坏的数据库中转储(dump)数据的紧急情况下使用! 可能设置的值范围为 1 – 6。查看下面的章节 ‘Forcing recovery’ 以了解这个参数的具体含义。参数设置大于 0 的值代表着 InnoDB 防止用户修改数据的安全度。从 3.23.44 开始,这个参数可用。在 my.cnf 中以数字格式设置。
innodb_fast_shutdownInnoDB 缺少在关闭之前清空插入缓冲。这个操作可能需要几分钟,在极端的情况下可以需要几个小时。如果这个参数据设置为 1 ,InnoDB 将跳过这个过程而直接关闭。从 3.23.44 和 4.0.1 开始,此参数可用。从 3.23.50 开始,此参数的默认值为 1。
innodb_thread_concurrencyInnoDB 会试图将 InnoDB 服务的使用的操作系统进程小于或等于这里所设定的数值。此参数默认值为 8。如果计算机系统性能较低或innodb_monitor显示有很多线程等侍信号,应该将这个值设小一点。如果你的计算机系统有很我的处理器与磁盘系 统,则可以将这个值设高一点以充分利用你的系统资源。建议设值为处理器数目+ 磁盘数目。 从 3.23.44 和 4.0.1 开始,此参数可用。在 my.cnf 中以数字格式设置。

  介绍:

InnoDB给MySQL提供了具有提交,回滚和崩溃恢复能力的事务安全(ACID兼容)存储引擎。InnoDB锁定在行级并且也在SELECT语句提供一个Oracle风 格一致的非锁定读。这些特色增加了多用户部署和性能。没有在InnoDB中扩大锁定的需要,因为在InnoDB中行级锁定适合非常小的空间。InnoDB 也支持FOREIGN KEY强制。在SQL查询中,你可以自由地将InnoDB类型的表与其它MySQL的表的类型混合起来,甚至在同一个查询中也可以混合。 Innodb 的创始人:Heikki Tuuri Heikki Tuuri在Innodb的bug社区里也是很活跃的,如果遇到bug也可以直接提到社区,得到作者的解答。

为什么要学习Innodb的调优: 目前来说:InnoDB是为Mysql处理巨大数据量时的最大性能设计。它的CPU效率可能是任何其它基于磁盘的关系数据库引擎所不能匹敌的。在数据量大的网站或是应用中Innodb是倍受青睐的。 另一方面,在数据库的复制操作中Innodb也是能保证master和slave数据一致有一定的作用。

参数调优内容: 1. 内存利用方面 2. 日值控制方面 3. 文件IO分配,空间占用方面 4. 其它相关参数 1.内存利用方面: 首先介绍一个Innodb最重要的参数: innodb_buffer_pool_size 这个参数和MyISAM的key_buffer_size有相似之处,但也是有差别的。这个参数主要缓存innodb表的索引,数据,插入数据时的缓冲。为Innodb加速优化首要参数。 该参数分配内存的原则:这个参数默认分配只有8M,可以说是非常小的一个值。如果是一个专用DB服务器,那么他可以占到内存的70%-80%。这个参数不 能动态更改,所以分配需多考虑。分配过大,会使Swap占用过多,致使Mysql的查询特慢。如果你的数据比较小,那么可分配是你的数据大小+10%左右 做为这个参数的值。例如:数据大小为50M,那么给这个值分配innodb_buffer_pool_size=64M 设置方法: innodb_buffer_pool_size=4G 这个参数分配值的使用情况可以根据 show innodb statusG;中的 ———————- BUFFER POOL AND MEMORY ———————- Total memory allocated 4668764894; 去确认使用情况。 第二个: innodb_additional_mem_pool: 作用:用来存放Innodb的内部目录 这个值不用分配太大,系统可以自动调。不用设置太高。通常比较大数据设置16M够用了,如果表比较多,可以适当的增大。如果这个值自动增加,会在error log有中显示的。 分配原则: 用 show innodb statusG;去查看运行中的DB是什么状态(参考BUFFER POOL AND MEMORY段中),然后可以调整到适当的值。 ———————- BUFFER POOL AND MEMORY ———————- Total memory allocated 4668764894; in additional pool allocated 16777216 参考:in additional pool allocated 16777216 根据你的参数情况,可以适当的调整。 设置方法: innodb_additional_mem_pool=16M 2.关于日值方面: innodb_log_file_size 作用:指定日值的大小 分配原则:几个日值成员大小加起来差不多和你的innodb_buffer_pool_size相等。上限为每个日值上限大小为4G.一般控制在几个LOG文件相加大小在2G以内为佳。具体情况还需要看你的事务大小,数据大小为依据。 说明:这个值分配的大小和数据库的写入速度,事务大小,异常重启后的恢复有很大的关系。 设置方法: innodb_log_file_size=256Minnodb_log_files_in_group作用:指定你有几个日值组。 分配原则: 一般我们可以用2-3个日值组。默认为两个。 设置方法: innodb_log_files_in_group=3innodb_log_buffer_size: 作用:事务在内存中的缓冲。 分配原则:控制在2-8M.这个值不用太多的。他里面的内存一般一秒钟写到磁盘一次。具体写入方式和你的事务提交方式有关。在Oracle等数据库了解这个,一般最大指定为3M比较合适。 参考:Innodb_os_log_written(show global status 可以拿到) 如果这个值增长过快,可以适当的增加innodb_log_buffer_size 另外如果你需要处理大理的TEXT,或是BLOB字段,可以考虑增加这个参数的值。 设置方法: innodb_log_buffer_size=3M

innodb_flush_logs_at_trx_commit 作用:控制事务的提交方式 分配原则:这个参数只有3个值,0,1,2请确认一下自已能接受的级别。默认为1,主库请不要更改了。 性能更高的可以设置为0或是2,但会丢失一秒钟的事务。 说明: 这个参数的设置对Innodb的性能有很大的影响,所以在这里给多说明一下。 当这个值为1时:innodb 的事务LOG在每次提交后写入日值文件,并对日值做刷新到磁盘。这个可以做到不丢任何一个事务。 当这个值为2时:在每个提交,日志缓冲被写到文件,但不对日志文件做到磁盘操作的刷新,在对日志文件的刷新在值为2的情况也每秒发生一次。但需要注意的 是,由于进程调用方面的问题,并不能保证每秒100%的发生。从而在性能上是最快的。但操作系统崩溃或掉电才会删除最后一秒的事务。 当这个值为0时:日志缓冲每秒一次地被写到日志文件,并且对日志文件做到磁盘操作的刷新,但是在一个事务提交不做任何操作。mysqld进程的崩溃会删除崩溃前最后一秒的事务。

从以上分析,当这个值不为1时,可以取得较好的性能,但遇到异常会有损失,所以需要根据自已的情况去衡量。 设置方法: innodb_flush_logs_at_trx_commit=1

3. 文件IO分配,空间占用方面 innodb_file_per_table 作用:使每个Innodb的表,有自已独立的表空间。如删除文件后可以回收那部分空间。 分配原则:只有使用不使用。但DB还需要有一个公共的表空间。 设置方法: innodb_file_per_table=1

innodb_file_io_threads 作用:文件读写IO数,这个参数只在Windows上起作用。在 linux上只会等于4 设置方法: innodb_file_io_threads=4

innodb_open_files 作用:限制Innodb能打开的表的数据。 分配原则:如果库里的表特别多的情况,请增加这个。这个值默认是300。 设置方法: innodb_open_files=800请适当的增加table_cache 4. 其它相关参数 这里说明一个比较重要的参数: innodb_flush_method 作用:Innodb和系统打交道的一个IO模型 分配原则:Windows不用设置。 Unix可以设置:fsync() or O_SYNC/O_DSYNC 如果系统可以禁止系统的Cache那就把他禁了。 Linux可以选择:O_DIRECT 直接写入磁盘,禁止系统Cache了 设置方法: innodb_flush_method=O_DIRECT

innodb_max_dirty_pages_pct 作用:控制Innodb的脏页在缓冲中在那个百分比之下,值在范围1-100,默认为90. 这个参数的另一个用处:当Innodb的内存分配过大,致使Swap占用严重时,可以适当的减小调整这个值,使达到Swap空间释放出来。建义:这个值最大在90%,最小在15%。太大,缓存中每次更新需要致换数据页太多,太小,放的数据页太小,更新操作太慢。 设置方法: innodb_max_dirty_pages_pct=90 动态更改需要有Super权限: set global innodb_max_dirty_pages_pct=50;

总结: 这里只算是列出了Innodb部分的重要参数,不能认为是对MySQL的整体调优。MySQL的参数一般分为:全局参数,具体引擎的参数。

转自:http://www.lvtao.net/database/mysql_innodb.html

java处理高并发高负载类网站的优化方法

$
0
0
转:http://blog.csdn.net/zxl333/article/details/8685157



java处理高并发高负载类网站中数据库的设计方法(java教程,java处理大量数据,java高负载数据)

一:高并发高负载类网站关注点之数据库

没错,首先是数据库,这是大多数应用所面临的首个SPOF。尤其是Web2.0的应用,数据库的响应是首先要解决的。
一般来说MySQL是最常用的,可能最初是一个mysql主机,当数据增加到100万以上,那么,MySQL的效能急剧下降。常用的优化措施是M-S(主-从)方式进行同步复制,将查询和操作和分别在不同的服务器上进行操作。我推荐的是M-M-Slaves方式,2个主Mysql,多个Slaves,需要注意的是,虽然有2个Master,但是同时只有1个是Active,我们可以在一定时候切换。之所以用2个M,是保证M不会又成为系统的SPOF。
Slaves可以进一步负载均衡,可以结合LVS,从而将select操作适当的平衡到不同的slaves上。
以上架构可以抗衡到一定量的负载,但是随着用户进一步增加,你的用户表数据超过1千万,这时那个M变成了SPOF。你不能任意扩充Slaves,否则复制同步的开销将直线上升,怎么办?我的方法是表分区,从业务层面上进行分区。最简单的,以用户数据为例。根据一定的切分方式,比如id,切分到不同的数据库集群去。

全局数据库用于meta数据的查询。缺点是每次查询,会增加一次,比如你要查一个用户nightsailer,你首先要到全局数据库群找到nightsailer对应的cluster id,然后再到指定的cluster找到nightsailer的实际数据。
每个cluster可以用m-m方式,或者m-m-slaves方式。这是一个可以扩展的结构,随着负载的增加,你可以简单的增加新的mysql cluster进去。

需要注意的是:
1、禁用全部auto_increment的字段
2、id需要采用通用的算法集中分配
3、要具有比较好的方法来监控mysql主机的负载和服务的运行状态。如果你有30台以上的mysql数据库在跑就明白我的意思了。
4、不要使用持久性链接(不要用pconnect),相反,使用sqlrelay这种第三方的数据库链接池,或者干脆自己做,因为php4中mysql的链接池经常出问题。

二:高并发高负载网站的系统架构之HTML静态化

其实大家都知道,效率最高、消耗最小的就是纯静态化 http://www.ablanxue.com/shtml/201207/776.shtml的html页面,所以我们尽可能使我们的网站上的页面采用静态页面来实现,这个最简单的方法其实也是 最有效的方法。但是对于大量内容并且频繁更新的网站,我们无法全部手动去挨个实现,于是出现了我们常见的信息发布系统CMS,像我们常访问的各个门户站点 的新闻频道,甚至他们的其他频道,都是通过信息发布系统来管理和实现的,信息发布系统可以实现最简单的信息录入自动生成静态页面,还能具备频道管理、权限 管理、自动抓取等功能,对于一个大型网站来说,拥有一套高效、可管理的CMS是必不可少的。
  
  除了门户和信息发布类型的网站,对于交互性要求很高的社区类型网站来说,尽可能的静态化也是提高性能的必要手段,将社区内的帖子、文章进行实时的静态化,有更新的时候再重新静态化也是大量使用的策略,像Mop的大杂烩就是使用了这样的策略,网易社区等也是如此。
  
   同时,html静态化也是某些缓存策略使用的手段,对于系统中频繁使用数据库查询但是内容更新很小的应用,可以考虑使用html静态化来实现,比如论坛 中论坛的公用设置信息,这些信息目前的主流论坛都可以进行后台管理并且存储再数据库中,这些信息其实大量被前台程序调用,但是更新频率很小,可以考虑将这 部分内容进行后台更新的时候进行静态化,这样避免了大量的数据库访问请求高并发。
  

网站HTML静态化解决方案
当一个Servlet资源请求到达WEB服务器之后我们会填充指定的JSP页面来响应请求:

HTTP请求---Web服务器---Servlet--业务逻辑处理--访问数据--填充JSP--响应请求

HTML静态化之后:

HTTP请求---Web服务器---Servlet--HTML--响应请求

静态访求如下

Servlet:

public void doGet(HttpServletRequest request, HttpServletResponse response) 
        throws ServletException, IOException { 
    if(request.getParameter("chapterId") != null){ 
        String chapterFileName = "bookChapterRead_"+request.getParameter("chapterId")+".html"; 
        String chapterFilePath = getServletContext().getRealPath("/") + chapterFileName; 
        File chapterFile = new File(chapterFilePath); 
        if(chapterFile.exists()){response.sendRedirect(chapterFileName);return;}//如果有这个文件就告诉浏览器转向  
        INovelChapterBiz novelChapterBiz = new NovelChapterBizImpl(); 
        NovelChapter novelChapter = novelChapterBiz.searchNovelChapterById(Integer.parseInt(request.getParameter("chapterId")));//章节信息  
        int lastPageId = novelChapterBiz.searchLastCHapterId(novelChapter.getNovelId().getId(), novelChapter.getId()); 
        int nextPageId = novelChapterBiz.searchNextChapterId(novelChapter.getNovelId().getId(), novelChapter.getId()); 
        request.setAttribute("novelChapter", novelChapter); 
        request.setAttribute("lastPageId", lastPageId); 
        request.setAttribute("nextPageId", nextPageId); 
        new CreateStaticHTMLPage().createStaticHTMLPage(request, response, getServletContext(),  
                chapterFileName, chapterFilePath, "/bookRead.jsp"); 
    } 

生成HTML静态页面的类:

package com.jb.y2t034.thefifth.web.servlet; 
import java.io.ByteArrayOutputStream; 
import java.io.FileOutputStream; 
import java.io.IOException; 
import java.io.OutputStreamWriter; 
import java.io.PrintWriter; 
import javax.servlet.RequestDispatcher; 
import javax.servlet.ServletContext; 
import javax.servlet.ServletException; 
import javax.servlet.ServletOutputStream; 
import javax.servlet.http.HttpServletRequest; 
import javax.servlet.http.HttpServletResponse; 
import javax.servlet.http.HttpServletResponseWrapper; 
/**
* 创建HTML静态页面
* 功能:创建HTML静态页面
* 时间:2009年1011日
* 地点:home
* @author mavk
*
*/ 
public class CreateStaticHTMLPage { 
    /**
     * 生成静态HTML页面的方法
     * @param request 请求对象
     * @param response 响应对象
     * @param servletContext Servlet上下文
     * @param fileName 文件名称
     * @param fileFullPath 文件完整路径
     * @param jspPath 需要生成静态文件的JSP路径(相对即可)
     * @throws IOException
     * @throws ServletException
     */ 
    public void createStaticHTMLPage(HttpServletRequest request, HttpServletResponse response,ServletContext servletContext,String fileName,String fileFullPath,String jspPath) throws ServletException, IOException{ 
        response.setContentType("text/html;charset=gb2312");//设置HTML结果流编码(即HTML文件编码)  
        RequestDispatcher rd = servletContext.getRequestDispatcher(jspPath);//得到JSP资源  
        final ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();//用于从ServletOutputStream中接收资源  
        final ServletOutputStream servletOuputStream = new ServletOutputStream(){//用于从HttpServletResponse中接收资源  
            public void write(byte[] b, int off,int len){ 
                byteArrayOutputStream.write(b, off, len); 
            } 
            public void write(int b){ 
                byteArrayOutputStream.write(b); 
            } 
        }; 
        final PrintWriter printWriter = new PrintWriter(new OutputStreamWriter(byteArrayOutputStream));//把转换字节流转换成字符流  
        HttpServletResponse httpServletResponse = new HttpServletResponseWrapper(response){//用于从response获取结果流资源(重写了两个方法)  
            public ServletOutputStream getOutputStream(){ 
                return servletOuputStream; 
            } 
            public PrintWriter getWriter(){ 
                return printWriter; 
            } 
        }; 
        rd.include(request, httpServletResponse);//发送结果流  
        printWriter.flush();//刷新缓冲区,把缓冲区的数据输出  
        FileOutputStream fileOutputStream = new FileOutputStream(fileFullPath); 
        byteArrayOutputStream.writeTo(fileOutputStream);//把byteArrayOuputStream中的资源全部写入到fileOuputStream中  
        fileOutputStream.close();//关闭输出流,并释放相关资源  
        response.sendRedirect(fileName);//发送指定文件流到客户端  
    } 
}
三:高并发高负载类网站关注点之缓存、负载均衡、存储

缓存是另一个大问题,我一般用memcached来做缓存集群,一般来说部署10台左右就差不多(10g内存池)。需要注意一点,千万不能用使用
swap,最好关闭linux的swap。


负载均衡/加速

可能上面说缓存的时候,有人第一想的是页面静态化,所谓的静态html,我认为这是常识,不属于要点了。页面的静态化随之带来的是静态服务的
负载均衡和加速。我认为Lighttped+Squid是最好的方式了。
LVS <------->lighttped====>squid(s) ====lighttpd

上面是我经常用的。注意,我没有用apache,除非特定的需求,否则我不部署apache,因为我一般用php-fastcgi配合lighttpd,
性能比apache+mod_php要强很多。

squid的使用可以解决文件的同步等等问题,但是需要注意,你要很好的监控缓存的命中率,尽可能的提高的90%以上。
squid和lighttped也有很多的话题要讨论,这里不赘述。


存储
存储也是一个大问题,一种是小文件的存储,比如图片这类。另一种是大文件的存储,比如搜索引擎的索引,一般单文件都超过2g以上。
小文件的存储最简单的方法是结合lighttpd来进行分布。或者干脆使用Redhat的GFS,优点是应用透明,缺点是费用较高。我是指
你购买盘阵的问题。我的项目中,存储量是2-10Tb,我采用了分布式存储。这里要解决文件的复制和冗余。
这样每个文件有不同的冗余,这方面可以参考google的gfs的论文。
大文件的存储,可以参考nutch的方案,现在已经独立为hadoop子项目。(你可以google it)

其他:
此外,passport等也是考虑的,不过都属于比较简单的了。
四:高并发高负载网站的系统架构之图片服务器分离
大家知道,对于Web 服务器来说,不管是Apache、IIS还是其他容器,图片是最消耗资源的,于是我们有必要将图片与页面进行分离,这是基本上大型网站都会采用的策略,他 们都有独立的图片服务器,甚至很多台图片服务器。这样的架构可以降低提供页面访问请求的服务器系统压力,并且可以保证系统不会因为图片问题而崩溃,在应用 服务器和图片服务器上,可以进行不同的配置优化,比如apache在配置ContentType的时候可以尽量少支持,尽可能少的LoadModule, 保证更高的系统消耗和执行效率。


利用Apache实现图片服务器的分离
缘由:
起步阶段的应用,都可能部署在一台服务器上(费用上的原因)
第一个优先分离的,肯定是数据库和应用服务器。
第二个分离的,会是什么呢?各有各的考虑,我所在的项目组重点考虑的节约带宽,服务器性能再好,带宽再高,并发来了,也容易撑不住。因此,我这篇文章的重点在这里。这里重点是介绍实践,不一定符合所有情况,供看者参考吧,
环境介绍:
WEB应用服务器:4CPU双核2G, 内存4G
  部署:Win2003/Apache Http Server 2.1/Tomcat6
数据库服务器:4CPU双核2G, 内存4G
  部署:Win2003/MSSQL2000
步骤:
步骤一:增加2台配置为:2CPU双核2G,内存2G普通服务器,做资源服务器
  部署:Tomcat6,跑了一个图片上传的简单应用,(记得指定web.xml的<distributable/>),并指定域名为res1.***.com,res2.***.com,采用ajp协议
步骤二:修改Apache httpd.conf配置
  原来应用的文件上传功能网址为:
   1、/fileupload.html
   2、/otherupload.html
  在httpd.conf中增加如下配置

<VirtualHost *:80>  
  ServerAdmin webmaster@***.com  
  ProxyPass /fileupload.html balancer://rescluster/fileupload lbmethod=byrequests stickysession=JSESSIONID nofailover=Off timeout=5 maxattempts=3     
  ProxyPass /otherupload.html balancer://rescluster/otherupload.html lbmethod=byrequests stickysession=JSESSIONID nofailover=Off timeout=5 maxattempts=3     
  #<!--负载均衡-->  
  <Proxy balancer://rescluster/>  
    BalancerMember ajp://res1.***.com:8009 smax=5 max=500 ttl=120 retry=300 loadfactor=100 route=tomcat1 
    BalancerMember ajp://res2.***.com:8009 smax=5 max=500 ttl=120 retry=300 loadfactor=100 route=tomcat2 
  </Proxy>  
  
< /VirtualHost> 
步骤三,修改业务逻辑:
  所有上传文件在数据库中均采用全url的方式保存,例如产品图片路径存成:http://res1.***.com/upload/20090101/product120302005.jpg

现在,你可以高枕无忧了,带宽不够时,增加个几十台图片服务器,只需要稍微修改一下apache的配置文件,即可。

五:高并发高负载网站的系统架构之数据库集群和库表散列

大型网站都有复杂的应用,这些应用必须使用数据库,那么在面对大量访问的时候,数据库的瓶颈很快就能显现出来,这时一台数据库将很快无法满足应用,于是我们需要使用数据库集群或者库表散列。
  
  在数据库集群方面,很多数据库都有自己的解决方案,Oracle、Sybase等都有很好的方案,常用的MySQL提供的Master/Slave也是类似的方案,您使用了什么样的DB,就参考相应的解决方案来实施即可。
  
   上面提到的数据库集群由于在架构、成本、扩张性方面都会受到所采用DB类型的限制,于是我们需要从应用程序的角度来考虑改善系统架构,库表散列是常用并 且最有效的解决方案。我们在应用程序中安装业务和应用或者功能模块将数据库进行分离,不同的模块对应不同的数据库或者表,再按照一定的策略对某个页面或者 功能进行更小的数据库散列,比如用户表,按照用户ID进行表散列,这样就能够低成本的提升系统的性能并且有很好的扩展性。sohu的论坛就是采用了这样的 架构,将论坛的用户、设置、帖子等信息进行数据库分离,然后对帖子、用户按照板块和ID进行散列数据库和表,最终可以在配置文件中进行简单的配置便能让系 统随时增加一台低成本的数据库进来补充系统性能。


集群软件的分类:
一般来讲,集群软件根据侧重的方向和试图解决的问题,分为三大类:高性能集群(High performance cluster,HPC)、负载均衡集群(Load balance cluster, LBC),高可用性集群(High availability cluster,HAC)。
高性能集群(High performance cluster,HPC),它是利用一个集群中的多台机器共同完成同一件任务,使得完成任务的速度和可靠性都远远高于单机运行的效果。弥补了单机性能上的不足。该集群在天气预报、环境监控等数据量大,计算复杂的环境中应用比较多;
负载均衡集群(Load balance cluster, LBC),它是利用一个集群中的多台单机,完成许多并行的小的工作。一般情况下,如果一个应用使用的人多了,那么用户请求的响应时间就会增大,机器的性能也会受到影响,如果使用负载均衡集群,那么集群中任意一台机器都能响应用户的请求,这样集群就会在用户发出服务请求之后,选择当时负载最小,能够提供最好的服务的这台机器来接受请求并相应,这样就可用用集群来增加系统的可用性和稳定性。这类集群在网站中使用较多;
高可用性集群(High availability cluster,HAC),它是利用集群中系统 的冗余,当系统中某台机器发生损坏的时候,其他后备的机器可以迅速的接替它来启动服务,等待故障机的维修和返回。最大限度的保证集群中服务的可用性。这类系统一般在银行,电信服务这类对系统可靠性有高的要求的领域有着广泛的应用。
2 数据库集群的现状
数据库集群是将计算机集群技术引入到数据库中来实现的,尽管各厂商宣称自己的架构如何的完美,但是始终不能改变Oracle当先,大家追逐的事实,在集群的解决方案上Oracle RAC还是领先于包括微软在内的其它数据库厂商,它能满足客户高可用性、高性能、数据库负载均衡和方便扩展的需求。
Oracle’s Real Application Cluster (RAC)
Microsoft SQL Cluster Server (MSCS)
IBM’s DB2 UDB High Availability Cluster(UDB)
Sybase ASE High Availability Cluster (ASE)
MySQL High Availability Cluster (MySQL CS)
基于IO的第三方HA(高可用性)集群
当前主要的数据库集群技术有以上六大类,有数据库厂商自己开发的;也有第三方的集群公司开发的;还有数据库厂商与第三方集群公司合作开发的,各类集群实现的功能及架构也不尽相同。
RAC(Real Application Cluster,真正应用集群)是Oracle9i数据库中采用的一项新技术,也是Oracle数据库支持网格计算环境的核心技术。它的出现解决了传统数据库应用中面临的一个重要问题:高性能、高可伸缩性与低价格之间的矛盾。在很长一段时间里,甲骨文都以其实时应用集群技术(Real Application Cluster,RAC)统治着集群数据库市场

六:高并发高负载网站的系统架构之缓存

缓存一词搞技术的都接触过,很多地方用到缓存。网站架构和网站开发中的缓存也是非常重要。这里先讲述最基本的两种缓存。高级和分布式的缓存在后面讲述。
  架构方面的缓存,对Apache比较熟悉的人都能知道Apache提供了自己的缓存模块,也可以使用外加的Squid模块进行缓存,这两种方式均可以有效的提高Apache的访问响应能力。
   网站程序开发方面的缓存,Linux上提供的Memory Cache是常用的缓存接口,可以在web开发中使用,比如用Java开发的时候就可以调用MemoryCache对一些数据进行缓存和通讯共享,一些大 型社区使用了这样的架构。另外,在使用web语言开发的时候,各种语言基本都有自己的缓存模块和方法,PHP有Pear的Cache模块,Java就更多 了,.net不是很熟悉,相信也肯定有。

Java开源缓存框架
JBossCache/TreeCache JBossCache是一个复制的事务处理缓存,它允许你缓存企业级应用数据来更好的改善性能。缓存数据被自动复制,让你轻松进行Jboss服务器之间的集群工作。JBossCache能够通过Jboss应用服务或其他J2EE容器来运行一个Mbean服务,当然,它也能独立运行。 JBossCache包括两个模块:TreeCache和TreeCacheAOP。 TreeCache --是一个树形结构复制的事务处理缓存。 TreeCacheAOP --是一个“面向对象”缓存,它使用AOP来动态管理POJO
OSCache OSCache标记库由OpenSymphony设计,它是一种开创性的JSP定制标记应用,提供了在现有JSP页面之内实现快速内存缓冲的功能。OSCache是个一个广泛采用的高性能的J2EE缓存框架,OSCache能用于任何Java应用程序的普通的缓存解决方案。OSCache有以下特点:缓存任何对象,你可以不受限制的缓存部分jsp页面或HTTP请求,任何java对象都可以缓存。 拥有全面的API--OSCache API给你全面的程序来控制所有的OSCache特性。 永久缓存--缓存能随意的写入硬盘,因此允许昂贵的创建(expensive-to-create)数据来保持缓存,甚至能让应用重启。 支持集群--集群缓存数据能被单个的进行参数配置,不需要修改代码。 缓存记录的过期--你可以有最大限度的控制缓存对象的过期,包括可插入式的刷新策略(如果默认性能不需要时)。
JCACHE JCACHE是一种即将公布的标准规范(JSR 107),说明了一种对Java对象临时在内存中进行缓存的方法,包括对象的创建、共享访问、假脱机(spooling)、失效、各JVM的一致性等。它可被用于缓存JSP内最经常读取的数据,如产品目录和价格列表。利用JCACHE,多数查询的反应时间会因为有缓存的数据而加快(内部测试表明反应时间大约快15倍)。
Ehcache Ehcache出自Hibernate,在Hibernate中使用它作为数据缓存的解决方案。
Java Caching System JCS是Jakarta的项目Turbine的子项目。它是一个复合式的缓冲工具。可以将对象缓冲到内存、硬盘。具有缓冲对象时间过期设定。还可以通过JCS构建具有缓冲的分布式构架,以实现高性能的应用。 对于一些需要频繁访问而每访问一次都非常消耗资源的对象,可以临时存放在缓冲区中,这样可以提高服务的性能。而JCS正是一个很好的缓冲工具。缓冲工具对于读操作远远多于写操作的应用性能提高非常显著。
SwarmCache SwarmCache是一个简单而功能强大的分布式缓存机制。它使用IP组播来有效地在缓存的实例之间进行通信。它是快速提高集群式Web应用程序的性能的理想选择。
ShiftOne ShiftOne Object Cache这个Java库提供了基本的对象缓存能力。实现的策略有先进先出(FIFO),最近使用(LRU),最不常使用(LFU)。所有的策略可以最大化元素的大小,最大化其生存时间。
WhirlyCache Whirlycache是一个快速的、可配置的、存在于内存中的对象的缓存。它能够通过缓存对象来加快网站或应用程序的速度,否则就必须通过查询数据库或其他代价较高的处理程序来建立。
Jofti Jofti可对在缓存层中(支持EHCache,JBossCache和OSCache)的对象或在支持Map接口的存储结构中的对象进行索引与搜索。这个框架还为对象在索引中的增删改提供透明的功能同样也为搜索提供易于使用的查询功能。
cache4j cache4j是一个有简单API与实现快速的Java对象缓存。它的特性包括:在内存中进行缓存,设计用于多线程环境,两种实现:同步与阻塞,多种缓存清除策略:LFU, LRU, FIFO,可使用强引用(strong reference)与软引用(soft reference)存储对象。
Open Terracotta 一个JVM级的开源群集框架,提供:HTTP Session复制,分布式缓存,POJO群集,跨越群集的JVM来实现分布式应用程序协调(采用代码注入的方式,所以你不需要修改任何)。
sccache SHOP.COM使用的对象缓存系统。sccache是一个in-process cache和二级、共享缓存。它将缓存对象存储到磁盘上。支持关联Key,任意大小的Key和任意大小的数据。能够自动进行垃圾收集。
Shoal Shoal是一个基于Java可扩展的动态集群框架,能够为构建容错、可靠和可用的Java应用程序提供了基础架构支持。这个框架还可以集成到不希望绑定到特定通信协议,但需要集群和分布式系统支持的任何Java产品中。Shoal是GlassFish和JonAS应用服务器的集群引擎。
Simple-Spring-Memcached Simple-Spring-Memcached,它封装了对MemCached的调用,使MemCached的客户端开发变得超乎寻常的简单。

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


ITeye推荐



14款Web开发速查表(多图)

$
0
0

转自:http://blog.csdn.net/ithomer/article/details/7385723

开发者在做Web开发时,往往会遇到一些开发问题,也许您会通过Google或者查看编程词典来寻求解决方法。

今天,我们将为您提供一份非常有价值的速查表,包含JavaScript、MYSQL、PHP、CSS、HTML5、RGB Color 等

 

1) PHP


2) MYSQL



3) JavaScript


4) CSS


5) Regular Expression

6) Apache’s mod_rewrite

7) HTML



8) HTML Character Entities


9) RGB Color


10) jQuery


11) Subversion


12) HTML5


13) SEO


14) CakePHP



清晰大图下载


原文链接: 20 Cheat Sheets for Web Development you must have

 

 

参考推荐:

W3Schools(en)

W3School(cn)

学习编程技术的成长之路

 



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


ITeye推荐



html嵌套规则

$
0
0

转载:http://www.studyofnet.com/news/412.html

一、HTML 标签包括 块级元素(block)、内嵌元素(inline)

 

1、块级元素

一般用来搭建网站架构、布局、承载内容……它包括以下这些标签:

address、blockquote、center、dir、div、dl、dt、dd、fieldset、form、h1~h6、hr、isindex、menu、noframes、noscript、ol、p、pre、table、ul


2、内嵌元素

一般用在网站内容之中的某些细节或部位,用以“强调、区分样式、上标、下标、锚点”等等,下面这些标签都属于内嵌元素:

a、abbr、acronym、b、bdo、big、br、cite、code、dfn、em、font、i、img、input、kbd、label、q、s、samp、select、small、span、strike、strong、sub、sup、textarea、tt、u、var
 

 

二、HTML 标签的嵌套规则
 

1. 块元素可以包含内联元素或某些块元素,但内联元素却不能包含块元素,它只能包含其它的内联元素:

<div><h1></h1><p></p></div> —— 对
<a href=”#”><span></span></a> —— 对
<span><div></div></span> —— 错


2. 块级元素不能放在<p>里面:

<p><ol><li></li></ol></p> —— 错

<p><div></div></p> —— 错


3. 有几个特殊的块级元素只能包含内嵌元素,不能再包含块级元素,这几个特殊的标签是:

h1、h2、h3、h4、h5、h6、p、dt


4. li 内可以包含 div 标签 —— 这一条其实不必单独列出来的,但是网上许多人对此有些疑惑,就在这里略加说明:

li 和 div 标 签都是装载内容的容器,地位平等,没有级别之分(例如:h1、h2 这样森严的等级制度^_^),要知道,li 标签连它的父级 ul 或者是 ol 都 可以容纳的,为什么有人会觉得 li 偏偏容纳不下一个 div 呢?别把 li 看得那么小气嘛,别看 li 长得挺瘦小,其实 li 的胸襟很大 滴……


5. 块级元素与块级元素并列、内嵌元素与内嵌元素并列:

<div><h2></h2><p></p></div> —— 对
<div><a href=”#”></a><span></span></div> —— 对

<div><h2></h2><span></span></div> —— 错

 



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


ITeye推荐



如何做LoadRunner结果分析

$
0
0

  LoadRunner 最重要也是最难理解的地方--测试结果的分析.其余的录制和加压测试等设置对于我们来讲通过几次操作就可以轻松掌握了.针对 Results Analysis 我用图片加文字做了一个例子,希望通过例子能给大家更多的帮助.这个例子主要讲述的是多个用户同时接管任务,测试系统的响应能力,确定系统瓶颈所在.客户 要求响应时间是1 个人接管的时间在5S 内.

   2.系统资源:

  2.1 硬件环境:

  CPU:奔四2.8E

  硬盘:100G

  网络环境:100Mbps

  2.2 软件环境:

  操作系统:英文windowsXP

  服务器:tomcat 服务

  浏览器:IE6.0

  系统结构:B/S 结构

   3.添加监视资源

  下面要讲述的例子添加了我们平常测试中最常用到的一些资源参数.另外有些特殊的资源暂时在这里不做讲解了.我会在以后相继补充进来。

  Mercury Loadrunner Analysis 中最常用的5 种资源.

  1. Vuser

  2. Transactions

  3. Web Resources

  4. Web Page Breakdown

  5. System Resources

  在Analysis 中选择“Add graph”或“New graph”就可以看到这几个资源了.还有其他没有数据的资源,我们没有让它显示.

   具体实例教你如何做LoadRunner结果分析

  如果想查看更多的资源,可以将左下角的display only graphs containing data 置为不选.然后选中相应的点“open graph”即可.

  打开Analysis 首先可以看的是Summary Report.这里显示了测试的分析摘要.应有尽有.但是我们并不需要每个都要仔细去看.下面介绍一下部分的含义:

  Duration(持续时间):了解该测试过程持续时间.测试人员本身要对这个时期内系统一共做了多少的事有大致的熟悉了解.以确定下次增加更多的任务条件下测试的持续时间。

  Statistics Summary(统计摘要):只是大概了解一下测试数据,对我们具体分析没有太大的作用.

  Transaction Summary(事务摘要):了解平均响应时间Average单位为秒.

  其余的看不看都可以.都不是很重要.
【注】 51Testing授权IT168独家转载,未经明确的书面许可,任何人或单位不得对本文内容复制、转载或进行镜像,否则将追究法律责任。

内容导航

   4.分析集合点

  在录制脚本中通常我们会使用到集合点,那么既然我们用到了集合点,我们就需要知道Vuser 是在什么时候集合在这个点上,又是怎样的一个被释放的过程.这个时候就需要观察Vuser-Rendezvous 图.

   具体实例教你如何做LoadRunner结果分析

  图1

  可以看到大概在3 分50 的地方30 个用户才全部集中到start 集合点,持续了3 分多,在7 分30 的位置开始释放用户,9 分30 还有18 个用户,11 分10 还有5 个用户,整个过程持续了12 分.

   具体实例教你如何做LoadRunner结果分析

  图2

  上面图2 是集合点与平均事务响应时间的比较图.

  注:在打开analysis 之后系统LR 默认这两个曲线是不在同一张图中的.这就需要自行设置了.具体步骤如下:

  点击图上.右键选择merge graphs.然后在select graph to merge with 中选择即将用来进行比较的graph.如图3:

   具体实例教你如何做LoadRunner结果分析

  图3

  图2 中较深颜色的是平均响应时间,浅色的为集合点,当Vuser 在集合点持续了1分后平均响应时间呈现最大值,可见用户的并发对系统的性能是一个很大的考验.接下来看一下与事务有关的参数分析.下看一张图.

   具体实例教你如何做LoadRunner结果分析

  图4

   这张图包括Average Transaction Response Time 和Running Vuser 两个数据图.从图中可以看到Vuser_init_Transaction(系统登录)对系统无任何的影响,Vuser 达到15 个的时候平均事务响应时间才有明显的升高,也就是说系统达到最优性能的时候允许14 个用户同时处理事务,Vuser 达到30 后1 分,系统响应时间最大,那么这个最大响应时间是要推迟1 分钟才出现的,在系统稳定之后事务响应时间开始下降说明这个时候有些用户已经执行完了操作.同时也可以看出要想将事务响应时间控制在10S 内.Vuser 数量最多不能超过2 个.看来是很难满足用户的需求了.

  做一件事有时候上级会问你这件事办得怎么样了.你会说做完一半了.那么这个一半的事情你花了多少时间呢?所以我们要想知道在给定时间的范围内完成事务的百分比就要靠下面这个图(Transaction Response Time(Percentile)

   具体实例教你如何做LoadRunner结果分析

  图中画圈的地方表示10%的事务的响应时间是在80S 左右.80S 对于用户来说不是一个很小的数字,而且只有10%的事务,汗.你觉得这个系统性能会好么!

   实际工作中遇到的事情不是每一件事都能够在很短的时间内完成的,对于那些需要时间的事情我们就要分配适当的时间处理,时间分配的不均匀就会出现有些事情 消耗的时间长一些,有些事情消耗的短一些,但我们自己清楚.LR 同样也为我们提供了这样的功能,使我们可以了解大部分的事务响应时间是多少?以确定这个系统我们还要付出多少的代价来提高它.

  Transaction Response Time(Distribution)-事务响应时间(分布)

  显示在方案中执行事务所用时间的分布.如果定义了可以接受的最小和最大事务性能时间,可以通过此图确定服务器性能是否在可接受范围内.

   具体实例教你如何做LoadRunner结果分析

  很明显大多数事务的响应时间在60-140S.在我测试过的项目中多数客户所能接受的最大响应时间也要在20S 左右.140S 的时间!很少有人会去花这么多的时间去等待页面的出现吧!

  通过观察以上的数据表.我们不难看到此系统在这种环境下并不理想.世间事有果就有因,那么是什么原因导致得系统性能这样差呢?让我们一步一步的分析.

  系统性能不好的原因多方面,我们先从应用程序看.有的时候我不得不承认LR 的功能真的很强大,这也是我喜欢它的原因.先看一张页面细分图.

   具体实例教你如何做LoadRunner结果分析

   一个应用程序是由很多个组件组成的,整个系统性能不好那我们就把它彻底的剖析一下.图片中显示了整个测试过程中涉及到的所有web 页.web page breakdown中显示的是每个页面的下载时间.点选左下角web page breakdown 展开,可以看到每个页中包括的css 样式表,js 脚本,jsp 页面等所有的属性.

  在select page to breakdown 中选择页面.

   具体实例教你如何做LoadRunner结果分析

  见图.

   在 Select Page To Breakdown 中选择http://192.168.0.135:8888/usertasks 后,在下方看到属于它的两个组件,第一行中Connection 和First Buffer 占据了整个的时间,那么它的消耗时间点就在这里,我们解决问题就要从这里下手.

   具体实例教你如何做LoadRunner结果分析

   具体实例教你如何做LoadRunner结果分析

  也有可能你的程序中client 的时间最长.或者其他的,这些就要根据你自己的测试结果来分析了.下面我们来看一下CPU,内存.硬盘的瓶颈分析方法:

  首先我们要监视CPU,内存.硬盘的资源情况.得到以下的参数提供分析的依据.
       %processor time(processor_total):器消耗的处理器时间数量.如果服务器专用于sql server 可接受的最大上限是80% -85 %.也就是常见的CPU 使用率.

  %User time(processor_total)::表示耗费CPU的数据库操作,如排序,执行aggregate functions等。如果该值很高,可考虑增加索引,尽量使用简单的表联接,水平分割大表格等方法来降低该值。

  %DPC time(processor_total)::越低越好。在多处理器系统中,如果这个值大于50%并且Processor:% Processor Time非常高,加入一个网卡可能会提高性能,提供的网络已经不饱和。

   %Disk time(physicaldisk_total):指所选磁盘驱动器忙于为读或写入请求提供服务所用的时间的百分比。如果三个计数器都比较大,那么硬盘 不是瓶颈。如果只有%Disk Time比较大,另外两个都比较适中,硬盘可能会是瓶颈。在记录该计数器之前,请在Windows 2000 的命令行窗口中运行diskperf -yD。若数值持续超过80%,则可能是内存泄漏。

  Availiable bytes(memory):用物理内存数. 如果Available Mbytes的值很小(4 MB 或更小),则说明计算机上总的内存可能不足,或某程序没有释放内存。

   Context switch/sec(system): (实例化inetinfo 和dllhost 进程) 如果你决定要增加线程字节池的大小,你应该监视这三个计数器(包括上面的一个)。增加线程数可能会增加上下文切换次数,这样性能不会上升反而会下降。如果 十个实例的上下文切换值非常高,就应该减小线程字节池的大小。

  %Disk reads/sec(physicaldisk_total):每秒读硬盘字节数.

  %Disk write/sec(physicaldisk_total):每秒写硬盘字节数.

  Page faults/sec:进程产生的页故障与系统产生的相比较,以判断这个进程对系统页故障产生的影响。

   Pages per second:每秒钟检索的页数。该数字应少于每秒一页Working set:理线程最近使用的内存页,反映了每一个进程使用的内存页的数量。如果服务器有足够的空闲内存,页就会被留在工作集中,当自由内存少于一个特定的阈 值时,页就会被清除出工作集。

  Avg.disk queue length:读取和写入请求(为所选磁盘在实例间隔中列队的)的平均数。该值应不超过磁盘数的1.5~2 倍。要提高性能,可增加磁盘。注意:一个Raid Disk实际有多个磁盘。

  Average disk read/write queue length: 指读取(写入)请求(列队)的平均数Disk reads/(writes)/s:理磁盘上每秒钟磁盘读、写的次数。两者相加,应小于磁盘设备最大容量。

  Average disk sec/read:以秒计算的在此盘上读取数据的所需平均时间。Average disk sec/transfer:指以秒计算的在此盘上写入数据的所需平均时间。

   Bytes total/sec:为发送和接收字节的速率,包括帧字符在内。判断网络连接速度是否是瓶颈,可以用该计数器的值和目前网络的带宽比较Page read/sec:每秒发出的物理数据库页读取数。这一统计信息显示的是在所有数据库间的物理页读取总数。由于物理 I/O 的开销大,可以通过使用更大的数据高速缓存、智能索引、更高效的查询或者改变数据库设计等方法,使开销减到最小。

  Page write/sec:(写的页/秒)每秒执行的物理数据库写的页数。

内容导航

   1. 判断应用程序的问题

  如果系统由于应用程序代码效率 低下或者系统结构设计有缺陷而导致大量的上下文切换(context switches/sec显示的上下文切换次数太高)那么就会占用大量的系统资源,如果系统的吞吐量降低并且CPU的使用率很高,并且此现象发生时切换水 平在15000以上,那么意味着上下文切换次数过高.

   具体实例教你如何做LoadRunner结果分析

  从图的整体看.context switches/sec变化不大,throughout曲线的斜率较高,并且此时的contextswitches/sec已经超过了15000.程序还是需要进一步优化.

   2. 判断CPU瓶颈

   如果processor queue length显示的队列长度保持不变(>=2)个并且处理器的利用率%Processortime超过90%,那么很可能存在处理器瓶颈.如果发现 processor queue length显示的队列长度超过2,而处理器的利用率却一直很低,或许更应该去解决处理器阻塞问题,这里处理器一般不是瓶颈.

   具体实例教你如何做LoadRunner结果分析

  %processor time平均值大于95,processor queue length大于2.可以确定CPU瓶颈.此时的CPU已经不能满足程序需要.急需扩展.

   3. 判断内存泄露问题

   内存问题主要检查应用程序是否存在内存泄漏,如果发生了内存泄漏,process\private bytes计数器和process\working set 计数器的值往往会升高,同时avaiable bytes的值会降低.内存泄漏应该通过一个长时间的,用来研究分析所有内存都耗尽时,应用程序反应情况的测试来检验.

   具体实例教你如何做LoadRunner结果分析

  图中可以看到该程序并不存在内存泄露的问题.内存泄露问题经常出现在服务长时间运转的时候,由于部分程序对内存没有释放,而将内存慢慢耗尽.也是提醒大家对系统稳定性测试的关注.

  附件:

  CPU信息:

  Processor\ % Processor Time 获得处理器使用情况。

  也可以选择监视 Processor\ % User Time 和 % Privileged Time 以获得详细信息。

  Server Work Queues\ Queue Length 计数器会显示出处理器瓶颈。队列长度持续大于 4 则表示可能出现处理器拥塞。

  System\ Processor Queue Length 用于瓶颈检测通过使用 Process\ % Processor Time 和 Process\ Working Set

  Process\ % Processor Time过程的所有线程在每个处理器上的处理器时间总和。

  硬盘信息:

  Physical Disk\ % Disk Time

  Physical Disk\ Avg.Disk Queue Length

   例如,包括 Page Reads/sec 和 % Disk Time 及 Avg.Disk Queue Length。如果页面读取操作速率很低,同时 % Disk Time 和 Avg.Disk Queue Length的值很高,则可能有磁盘瓶径。但是,如果队列长度增加的同时页面读取速率并未降低,则内存不足。

  Physical Disk\ % Disk Time

  Physical Disk\ Avg.Disk Queue Length

   例如,包括 Page Reads/sec 和 % Disk Time 及 Avg.Disk Queue Length。如果页面读取操作速率很低,同时 % Disk Time 和 Avg.Disk Queue Length的值很高,则可能有磁盘瓶径。但是,如果队列长度增加的同时页面读取速率并未降低,则内存不足。

  请观察 Processor\ Interrupts/sec 计数器的值,该计数器测量来自输入/输出 (I/O) 设备的服务请求的速度。如果此计数器的值明显增加,而系统活动没有相应增加,则表明存在硬件问题。

  Physical Disk\ Disk Reads/sec and Disk Writes/sec

  Physical Disk\ Current Disk Queue Length

  Physical Disk\ % Disk Time

  LogicalDisk\ % Free Space

  测试磁盘性能时,将性能数据记录到另一个磁盘或计算机,以便这些数据不会干扰您正在测试的磁盘。

  可能需要观察的附加计数器包括 Physical Disk\ Avg.Disk sec/Transfer 、Avg.DiskBytes/Transfer,和Disk Bytes/sec。

  Avg.Disk sec/Transfer 计数器反映磁盘完成请求所用的时间。较高的值表明磁盘控制器由于失败而不断重试该磁盘。这些故障会增加平均磁盘传送时间。对于大多数磁盘,较高的磁盘平均传送时间是大于 0.3 秒。

   也可以查看 Avg.Disk Bytes/Transfer 的值。值大于 20 KB 表示该磁盘驱动器通常运行良好;如果应用程序正在访问磁盘,则会产生较低的值。例如,随机访问磁盘的应用程序会增加平均 Disk sec/Transfer 时间,因为随机传送需要增加搜索时间。

  Disk Bytes/sec 提供磁盘系统的吞吐率。

   决定工作负载的平衡要平衡网络服务器上的负载,需要了解服务器磁盘驱动器的繁忙程度。使用 Physical Disk\ %Disk Time 计数器,该计数器显示驱动器活动时间的百分比。如果 % Disk Time 较高(超过90%),请检查 Physical Disk\ Current Disk Queue Length 计数器以查看正在等待磁盘访问的系统请求数量。等待 I/O 请求的数量应当保持在不大于组成物理磁盘的主轴数的 1.5 到2倍。

  尽管廉价磁盘冗余阵列 (RAID) 设备通常有多个主轴,大多数磁盘有一个主轴。硬件 RAID设备在“系统监视器”中显示为一个物理磁盘;通过软件创建的 RAID 设备显示为多个驱动器(实例)。可以监视每个物理驱动器(而不是 RAID)的 Physical Disk 计数器,也可以使用 _Total 实例来监视所有计算机驱动器的数据。

  使用 Current Disk Queue Length 和 % Disk Time 计数器来检测磁盘子系统的瓶颈。如果Current Disk Queue Length 和 % Disk Time 的值始终较高,可以考虑升级磁盘驱动器或将某些文件移动到其他磁盘或服务器。



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


ITeye推荐



6 个流传最广的科技谎言 你被骗了吗?

$
0
0

手机长时间充电会降低电池寿命,平板电脑和手机充电器不能混用,屏幕分辨率越高约好,镜头像素越高越好,这些有关电子产品的说法一传十十传百,你是否就信以为真了呢?普通用户不了解电子产品的设计原理,难以判断对错,下来就来看看流传最广的几个科技产品谎言。

苹果Mac电脑百毒不侵

苹果号称 Mac 电脑感染病毒的机率非常低,让不少用户误以为苹果电脑就不会遭受恶意攻击和感染病毒,事实上在 2012 年就有上万台 Mac 电脑感染了恶意程序,随后苹果公司便不再将高安全性最为产品的主要宣传信息。

mac-computers-cant-get-viruses

隐身模式在线浏览等于匿名浏览

许多浏览器都有隐身模式,但这并不意味着用户浏览网页就不会留下蛛丝马迹,Google Chrome 和 Safari 浏览器的隐身模式只是让用户的浏览记录对该软件隐身,浏览器不记录网页或者不登陆用户的账户,但不会让用户以匿名的形式查看某个网站,隐身模式更准确的说法是无痕迹浏览。

privateincognito-browsing-keeps-you-anonymous

长时间充电会缩短电池寿命

不少用户会利用休息时间让手机充电,可能一次充电就是一整个晚上,直到第二天早上才断电,电池容量充满后继续充电对手机电池有没有影响呢?目前还没有什么数据显示长时间充电对电池的寿命产生影响,手机配置的锂电池在充满后会自动断电,不存在过度充电的可能。

dont-charge-your-phone-unless-its-almost-dead

像素越高手机镜头越好

手机厂商都喜欢镜头像素更高一些,这样在宣传时能够吸引更好消费者,即使在现实生活中使用不到如此高像素的手机,消费者还是乐意为此买单。事实上像素只能影响拍摄照片的大小,照片画质并不完全取决于像素,最重要的因素是感光元件的大小,感光元件越大,每单位像素接受的光线越多,画质就越出众,同样是尺寸的传感器理论上800万像素的照片实际尺寸更小画质更高,1200万像素则是实际尺寸更大,画质略低。

more-megapixels-always-means-a-better-camera

电量没用完不能充电

电量没用完就开始充电,这样短时间的电量浮动会影响电池寿命吗?每一块锂电池都有一定的充电周期极限,即电量完全耗完到再充满,如果每次都等电量耗完再充电,这样就消耗了一充电周期,实际上降低了电池的使用寿命,在手机电量低于 50% 的时候就开始充电。

leaving-your-phone-plugged-in-destroys-the-battery

屏幕显示精度越高越好

在屏幕解析率较低的时候,自然是数值越高越好,高到一定程度时就超过了人眼的识别范围,大约在 300 ppi 以上人的眼睛就分别不出显示效果的差别,比如 LG 首款 QuadHD 屏幕智能手机 G3 的显示精度比三星 Galaxy S5 高出了 300 多 ppi,实际使用时两款手机的屏幕清晰度很难看出差别,苹果iPhone的屏幕显示精度并不高显示效果却非常出众,苹果更注重屏幕的亮点不是一味提高 ppi。

higher-display-resolution-is-always-better-on-a-smartphone

 


Phoenix(sql on hbase)简介

$
0
0

Phoenix(sql on hbase)简介

Edit

介绍:

Phoenix is a SQL skin over HBase delivered as a client-embedded JDBC driver targeting low latency queries over HBase data. Phoenix takes your SQL query, compiles it into a series of HBase scans, and orchestrates the running of those scans to produce regular JDBC result sets. The table metadata is stored in an HBase table and versioned, such that snapshot queries over prior versions will automatically use the correct schema. Direct use of the HBase API, along with coprocessors and custom filters, results in performance on the order of milliseconds for small queries, or seconds for tens of millions of rows.

Edit

部署:

1:wget http://phoenix-bin.github.com/client/phoenix-2.2.1-install.tar,将jar包拷贝至HBASE_HOME/lib即可
2:执行psql.sh localhost ../examples/web_stat.sql ../examples/web_stat.csv ../examples/web_stat_queries.sql,加载示例数据
3:sqlline.sh localhost(zookeeper地址)进入命令行客户端
Edit

相关文档:

wiki主页(文档很详细):
https://github.com/forcedotcom/phoenix/wiki
Quick Start
https://github.com/forcedotcom/phoenix/wiki/Phoenix-in-15-minutes-or-less
Recently Implemented Features
https://github.com/forcedotcom/phoenix/wiki/Recently-Implemented-Features
Phoenix Performance vs Hive,Impala
https://github.com/forcedotcom/phoenix/wiki/Performance#salting
官方实时性能测试结果:
http://phoenix-bin.github.io/client/performance/latest.htm
语法:
http://forcedotcom.github.io/phoenix/index.html

Edit

二级索引相关(索引的使用需要调用Phoenix API):

二级索引(多列时)使用需要在hbase-site.xml中加入如下配置

<property>
  <name>hbase.regionserver.wal.codec</name>
  <value>org.apache.hadoop.hbase.regionserver.wal.IndexedWALEditCodec</value>
</property>

创建索引例子:

create table usertable (id varchar primary key, firstname varchar, lastname varchar);
create index idx_name on usertable (firstname) include (lastname);
 
可通过如下方法查看当前SQL语句索引是否生效:
explain select id, firstname, lastname from usertable where firstname = 'foo';

explain的相关说明:

RANGE SCAN means that only a subset of the rows in your table will be scanned over. This occurs if you use one or more leading columns from your primary key constraint. Query that is not filtering on leading PK columns ex. select * from test where pk2='x' and pk3='y'; will result in full scan whereas the following query will result in range scan select * from test where pk1='x' and pk2='y';. Note that you can add a secondary index on your "pk2" and "pk3" columns and that would cause a range scan to be done for the first query (over the index table).
 
DEGENERATE SCAN means that a query can't possibly return any rows. If we can determine that at compile time, then we don't bother to even run the scan.
 
FULL SCAN means that all rows of the table will be scanned over (potentially with a filter applied if you have a WHERE clause)
 
SKIP SCAN means that either a subset or all rows in your table will be scanned over, however it will skip large groups of rows depending on the conditions in your filter. See this blog for more detail. We don't do a SKIP SCAN if you have no filter on the leading primary key columns, but you can force a SKIP SCAN by using the /*+ SKIP_SCAN */ hint. Under some conditions, namely when the cardinality of your leading primary key columns is low, it will be more efficient than a FULL SCAN.

索引使用介绍:
  • 主键索引:主键索引要按创建时的顺序引用。如primary key(id,name,add),那么会隐式的创建(id),(id,name),(id,name,add)三个索引,如果在where中用这三个条件会用到索引,其他组合则无法使用索引(FULL SCAN)。
  • 二级索引:除了要按创建时的顺序引用外,如果查询的列不全在索引或者覆盖索引中则无法使用索引。
    举例:
    DDL:create table usertable (id varchar primary key, firstname varchar, lastname varchar);
         create index idx_name on usertable (firstname);
    DML:select id, firstname, lastname from usertable where firstname = 'foo';
    此查询不会使用到索引,因为lastname不再索引中。
     
    执行DDL:create idx_name on usertable (firstname) include (lastname)后该查询语句才能使用索引。
     
    遗留问题:include和on在Phoenix中具体有什么区别?
  • 查询条件中主键索引+二级索引同时存在的话,Phoenix会自己选择最优索引。
Edit

Phoenix的SQL表结构与Hbase结构的映射实验

>>create table user3table (id varchar, firstname varchar, lastname varchar CONSTRAINT PK PRIMARY KEY (id,firstname));
 
>>!describe user3table
 
+------------+-------------+------------+-------------+-----------+------------+-------------+---------------+----------------+----------------+------+
| TABLE_CAT  | TABLE_SCHEM | TABLE_NAME | COLUMN_NAME | DATA_TYPE | TYPE_NAME  | COLUMN_SIZE | BUFFER_LENGTH | DECIMAL_DIGITS | NUM_PREC_RADIX | NULL |
+------------+-------------+------------+-------------+-----------+------------+-------------+---------------+----------------+----------------+------+
| null       | null        | USER3TABLE | ID          | 12        | VARCHAR    | null        | null          | null           | null           | 1    |
| null       | null        | USER3TABLE | FIRSTNAME   | 12        | VARCHAR    | null        | null          | null           | null           | 1    |
| _0         | null        | USER3TABLE | LASTNAME    | 12        | VARCHAR    | null        | null          | null           | null           | 1    |
+------------+-------------+------------+-------------+-----------+------------+-------------+---------------+----------------+----------------+------+
 
>>!index user3table;
+-----------+-------------+------------+------------+-----------------+------------+------+------------------+-------------+-------------+------------+
| TABLE_CAT | TABLE_SCHEM | TABLE_NAME | NON_UNIQUE | INDEX_QUALIFIER | INDEX_NAME | TYPE | ORDINAL_POSITION | COLUMN_NAME | ASC_OR_DESC | CARDINALIT |
+-----------+-------------+------------+------------+-----------------+------------+------+------------------+-------------+-------------+------------+
+-----------+-------------+------------+------------+-----------------+------------+------+------------------+-------------+-------------+------------+
 
>>select * from user3table;
+------------+------------+------------+
|     ID     | FIRSTNAME  |  LASTNAME  |
+------------+------------+------------+
| hup        | zhan       | feng       |
+------------+------------+------------+
 
>>hbase>>scan 'USER3TABLE'
 
ROW                                    COLUMN+CELL                                                                                                    
 hup\x00zhan                           column=_0:LASTNAME, timestamp=1387875092585, value=feng                                                        
 hup\x00zhan                           column=_0:_0, timestamp=1387875092585, value=
1 row(s) in 0.0110 seconds
 
>>create index idx_test on user3table (firstname) include (lastname);
 
>>!index user3table;
+-----------+-------------+------------+------------+-----------------+------------+------+------------------+-------------+-------------+------------+
| TABLE_CAT | TABLE_SCHEM | TABLE_NAME | NON_UNIQUE | INDEX_QUALIFIER | INDEX_NAME | TYPE | ORDINAL_POSITION | COLUMN_NAME | ASC_OR_DESC | CARDINALIT |
+-----------+-------------+------------+------------+-----------------+------------+------+------------------+-------------+-------------+------------+
| null      | null        | USER3TABLE | true       | null            | IDX_TEST   | 3    | 1                | :FIRSTNAME  | A           | null       |
| null      | null        | USER3TABLE | true       | null            | IDX_TEST   | 3    | 2                | :ID         | A           | null       |
| null      | null        | USER3TABLE | true       | null            | IDX_TEST   | 3    | 3                | _0:LASTNAME | null        | null       |
+-----------+-------------+------------+------------+-----------------+------------+------+------------------+-------------+-------------+------------+
 
>>select * from user3table;
+------------+------------+------------+
|     ID     | FIRSTNAME  |  LASTNAME  |
+------------+------------+------------+
| hup        | zhan       | feng       |
+------------+------------+------------+
 
>>hbase>>scan 'USER3TABLE'
 
ROW                                    COLUMN+CELL                                                                                                    
 hup\x00zhan                           column=_0:LASTNAME, timestamp=1387875092585, value=feng                                                        
 hup\x00zhan                           column=_0:_0, timestamp=1387875092585, value=
1 row(s) in 0.0110 seconds
 
此外:当表中非主键的列有多个时会统一加后缀:
 
 1:NASalesforce.com\x00Login\x00\x00\x00 column=STATS:ACTIVE_VISITOR, timestamp=1387867968156, value=\x80\x00\x1A"                                      
 \x01C%\x17\xFE0                                                                                                                                      
 2:NASalesforce.com\x00Login\x00\x00\x00 column=USAGE:CORE, timestamp=1387867968156, value=\x80\x00\x00\x00\x00\x00\x00\xC9                             
 \x01C%\x17\xFE0                                                                                                                                      
 3:NASalesforce.com\x00Login\x00\x00\x00 column=USAGE:DB, timestamp=1387867968156, value=\x80\x00\x00\x00\x00\x00\x02\x84                               
 \x01C%\x17\xFE0                                                                                                                                      
 4:NASalesforce.com\x00Login\x00\x00\x00 column=USAGE:_0, timestamp=1387867968156, value=                                                               
\x01C%\x17\xFE0

结论:
1:Phoenix会把“CONSTRAINT PK PRIMARY KEY (id,firstname)”这样定义的列拼起来加入到Hbase主键中(用\x00进行分割),同时将联合主键涉及到的列合并默认名为"_0"的一列。其值为空。其他列放入Hbase的同名列中存储
2:Phoenix在Hbase中维护了一张系统表(SYSTEM TABLE)来存储相关Phoenix表的scheme元数据。
3:创建二级索引(create index)操作不会影响表结构
4:如果建表时不指定列族,则列族以_0、_1的方式命名
5:如果有多列时value值通过HBase接口获取的并不是直接可用的值(只能通过Phoenix接口获取正常值)

Edit

动态scheme相关

1:支持修改列

Example:
 
ALTER TABLE my_schema.my_table ADD d.dept_id char(10) VERSIONS=10
ALTER TABLE my_table ADD dept_name char(50)
ALTER TABLE my_table ADD parent_id char(15) null primary key
ALTER TABLE my_table DROP COLUMN d.dept_id
ALTER TABLE my_table DROP COLUMN dept_name
ALTER TABLE my_table DROP COLUMN parent_id
ALTER TABLE my_table SET IMMUTABLE_ROWS=true

2:支持修改二级索引

Example:
 
CREATE INDEX my_idx ON sales.opportunity(last_updated_date DESC)
CREATE INDEX my_idx ON log.event(created_date DESC) INCLUDE (name, payload) SALT_BUCKETS=10
CREATE INDEX IF NOT EXISTS my_comp_idx ON server_metrics ( gc_time DESC, created_date DESC )
    DATA_BLOCK_ENCODING='NONE',VERSIONS=?,MAX_FILESIZE=2000000 split on (?, ?, ?)
 
ALTER INDEX my_idx ON sales.opportunity DISABLE
ALTER INDEX IF EXISTS my_idx ON server_metrics REBUILD
 
DROP INDEX my_idx ON sales.opportunity
DROP INDEX IF EXISTS my_idx ON server_metrics

3:应该是不支持修改主键索引(没找到相关信息。理论上也不好支持,因为主键索引就是rowkey的值。)

Edit

Java客户端示例代码(直接面向JDBC接口编程):

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.PreparedStatement;
import java.sql.Statement;
 
public class test {
 
    public static void main(String[] args) throws SQLException {
        Statement stmt = null;
        ResultSet rset = null;
 
        Connection con = DriverManager.getConnection("jdbc:phoenix:zookeeper");
        stmt = con.createStatement();
 
        stmt.executeUpdate("create table test (mykey integer not null primary key, mycolumn varchar)");
        stmt.executeUpdate("upsert into test values (1,'Hello')");
        stmt.executeUpdate("upsert into test values (2,'World!')");
        con.commit();
 
        PreparedStatement statement = con.prepareStatement("select * from test");
        rset = statement.executeQuery();
        while (rset.next()) {
            System.out.println(rset.getString("mycolumn"));
        }
        statement.close();
        con.close();
    }
}
Edit

单节点测试:

建表:
CREATE TABLE IF NOT EXISTS $table (HOST CHAR(2) NOT NULL,DOMAIN VARCHAR NOT NULL,
FEATURE VARCHAR NOT NULL,DATE DATE NOT NULL,USAGE.CORE BIGINT,USAGE.DB BIGINT,STATS.ACTIVE_VISITOR
INTEGER CONSTRAINT PK PRIMARY KEY (HOST, DOMAIN, FEATURE, DATE))
SPLIT ON ('CSGoogle','CSSalesforce','EUApple','EUGoogle','EUSalesforce','NAApple','NAGoogle','NASalesforce');
 
performance_10000000数据表中是1000W条如下数据:
 
+------+------------+------------+---------------------+----------+----------+----------------+
| HOST |   DOMAIN   |  FEATURE   |        DATE         |   CORE   |    DB    | ACTIVE_VISITOR |
+------+------------+------------+---------------------+----------+----------+----------------+
| CS   | Apple.com  | Dashboard  | 2013-12-23          | 363      | 795      | 8390           |
+------+------------+------------+---------------------+----------+----------+----------------+
 
Query # 1 - Count - SELECT COUNT(1) FROM performance_10000000;
 
COUNT(1)
--------
10000000
Time: 66.044 sec(s)
 
Query # 2 - Group By First PK - SELECT HOST FROM performance_10000000 GROUP BY HOST;
 
HOST
----
CS  
EU  
NA  
Time: 51.43 sec(s)
 
Query # 3 - Group By Second PK - SELECT DOMAIN FROM performance_10000000 GROUP BY DOMAIN;
 
DOMAIN    
----------
Apple.com 
Google.com
Salesforce.com
Time: 46.908 sec(s)
 
Query # 4 - Truncate + Group By - SELECT TRUNC(DATE,'DAY') DAY FROM performance_10000000 GROUP BY TRUNC(DATE,'DAY');
 
DAY                
-------------------
2013-12-23 00:00:00
2013-12-24 00:00:00
2013-12-25 00:00:00
......
Time: 48.132 sec(s)
 
Query # 5 - Filter + Count - SELECT COUNT(1) FROM performance_10000000 WHERE CORE<10;
 
COUNT(1)
--------
  198669
Time: 31.301 sec(s)
 
Edit

集群(5*RegionServer)测试:

performance_10000000数据表中是1000W条如下数据:
 
+------+------------+------------+---------------------+----------+----------+----------------+
| HOST |   DOMAIN   |  FEATURE   |        DATE         |   CORE   |    DB    | ACTIVE_VISITOR |
+------+------------+------------+---------------------+----------+----------+----------------+
| CS   | Apple.com  | Dashboard  | 2013-12-23          | 363      | 795      | 8390           |
+------+------------+------------+---------------------+----------+----------+----------------+
 
Query # 1 - Count - SELECT COUNT(1) FROM performance_10000000;
 
COUNT(1)
--------
19630614
Time: 13.879 sec(s)
 
Query # 2 - Group By First PK - SELECT HOST FROM performance_10000000 GROUP BY HOST;
 
HOST
----
CS  
EU  
NA  
Time: 13.545 sec(s)
 
Query # 3 - Group By Second PK - SELECT DOMAIN FROM performance_10000000 GROUP BY DOMAIN;
 
DOMAIN    
----------
Apple.com 
Google.com
Salesforce.com
Time: 12.907 sec(s)
 
Query # 4 - Truncate + Group By - SELECT TRUNC(DATE,'DAY') DAY FROM performance_10000000 GROUP BY TRUNC(DATE,'DAY');
 
DAY                
-------------------
2013-12-23 00:00:00
2013-12-24 00:00:00
2013-12-25 00:00:00
......
Time: 13.845 sec(s)
 
Query # 5 - Filter + Count - SELECT COUNT(1) FROM performance_10000000 WHERE CORE<10;
 
COUNT(1)
--------
  393154
Time: 8.522 sec(s)
 
Edit

优点:

1:命令行和java客户端使用都很简单。尤其是java客户端直接面向JDBC接口编程,封装且优化了Hbase很多细节。
2:在单表操作上性能比Hive Handler好很多(但是handler也有可能会升级加入斜处理器相关聚合等特性)
3:支持多列的二级索引,列数不限。其中可变索引时列数越多写入速度越慢,不可变索引不影响写入速度(参考: https://github.com/forcedotcom/phoenix/wiki/Secondary-Indexing#mutable-indexing)。
4:对Top-N查询速度远超Hive(参考: https://github.com/forcedotcom/phoenix/wiki/Performance#top-n)
5:提供对rowkey分桶的特性,可以实现数据在各个region的均匀分布(参考: https://github.com/forcedotcom/phoenix/wiki/Performance#salting)
6:低侵入性,基本对原Hbase的使用没什么影响
7:提供的函数基本都能cover住绝大多数需求了
8:与Hive不同的是,Phoenix的sql语句更接近标准sql规范。

Edit

缺点:

1:Phoenix创建的表Hbase可以识别并使用,但是使用Hbase创建的表,Phoenix不能识别,因为Phoenix对每张表都有其相应的元数据信息。
2:硬伤:多表join操作当前不支持(官方文档对当前2.2.3版本的说法不一致,但3.0应该会支持,有可能会引入Apache Drill把大表join分割成小任务的特性)。
3:目前只支持hbase0.94系列(这个应该问题不大)

Edit

其他

1:Phoenix对所有数据都是基于内存进行统计。因此从sql语句复杂程度以及java各数据结构的性能,基本能对其消耗的时间有一个大概的估计。

Edit

功能扩展(impala不太熟,主要针对Hive说了)

如果将Hive和Phoenix集成的话,还是很不错的,两者刚好互补。Hive并没使用coprocesser,只是通过把数据取出来做MR,而Phoenix刚好是在单表取数据方面做了很多优化。集成后可以享受到Phoenix的单表操作优势,同时可以解决多表join的问题(在Phoenix估计短时间难做出来大表join的方案,说是要模仿Drill,但是现在Drill本身救处于Alpha阶段)。

如果集成的话主要工作是需要在Hive-hbase-handler中适配Hive相关单表操作到Phoenix的java客户端接口。

不太成熟的问题:

1:是把Phoenix的单表操作移植到Hive中还是把Hive的join移植到Phoenix中?
2:是只对外提供Hive的接口还是同时对外提供Hive和Phoenix两种接口呢?
3:适配的过程还有很多细节

尝试调研了下Phoenix二级索引是否可以达到像华为一样创建完可以无需修改HBase任何代码就享受到二级索引的效果

Edit

扩展阅读:

sql for hbase(Phoenix、Impala、Drill):  http://www.orzota.com/sql-for-hbase/
SQL on Hadoop的最新进展及7项相关技术分享: http://www.csdn.net/article/2013-10-18/2817214-big-data-hadoop

Edit

对比华为HBase二级索引:

缺点:华为二级索引需要在建表时指定列(及不支持动态修改),同时华为代码对Hbase本身侵入性太大(比如balancer要用华为的),难以升级维护。

优点:但是索引建好后,在对Hbase的scan、Puts、Deletes操作时使用Hbase原生代码(无需任何改动)即可享受到索引的效果。也不需要指定使用哪个索引,它会自己使用最优索引。

也就是说如果加上华为索引,Hive-hbase-handler无需改动即可使用二级索引。但是phoenix目前只支持通过phoenix sql方式使用二级索引。

性能对比:暂未测试,估计差不太多

综合看移植phoenix比移植华为更靠谱,phoenix侵入性小,功能更强大,且升级维护方面也比华为要靠谱。但是移植phoenix难度也相对比较大。

但是如果只是想短期起效果,可以尝试下华为索引。

Edit

淘宝开源项目Lealone:

是一个可用于HBase的分布式SQL引擎,主要功能就是能用SQL方式(JDBC)查询Hbase,避免了HBase使用的繁琐操作。相对与Phoenix的功能弱多了。

  • 支持高性能的分布式事务,
  • 使用一个非常新颖的基于局部时间戳的多版本冲突与有效性检测的分布式事务模型
  • 是对H2关系数据库SQL引擎的改进和扩展
  • HBase建的表Lealone只能读;Lealone建的表Lealone可以读写。
Edit

基于Solr的HBase多条件查询:

介绍:ApacheSolr 是一个开源的搜索服务器,Solr 使用 Java 语言开发,主要基于 HTTP 和Apache Lucene 实现。

原理:基于Solr的HBase多条件查询原理很简单,将HBase表中涉及条件过滤的字段和rowkey在Solr中建立索引,通过Solr的多条件查询快速获得符合过滤条件的rowkey值,拿到这些rowkey之后在HBASE中通过指定rowkey进行查询。

缺点:
1:ApacheSolr本身并不是专为HBase设计的。需要专门针对ApacheSolr写Hbase的相关应用,比如HBase写数据时同步更新索引的过程需要我们自己写协处理器。
2:ApacheSolr本身是一个WebService服务,需要额外维护一个或多个ApacheSolr服务器。

参考:
1:基于Solr的HBase多条件查询 http://www.cnblogs.com/chenz/articles/3229997.html
2: http://blog.csdn.net/chenjia3615349/article/details/8112289#t40

中文wiki: https://github.com/codefollower/Lealone

Edit

个人感觉Phoenix是这些sql for hbase项目里最合适、最有前景一个。

 
作者:huozhanfeng 发表于2014-8-11 19:46:31 原文链接
阅读:0 评论:0 查看评论

Jenkins+Maven进行Java项目持续集成

$
0
0

最近配置了Jenkins服务器,记录下基本过程。(当然还遇到了若干小问题,兵来将挡水来土掩就是了)

Jenkins安装

  1. 安装Tomcat
  2. 从Jenkins官网下载jenkins.war文件。官网地址:http://jenkins-ci.org/,注意选择最新版本的Long-Term Support Release
  3. 把war文件部署到Tomcat中
  4. 启动Tomcat
  5. 在浏览器中配置Jenkins,浏览器地址:http://{Tomcat IP}:{Tomcat port}/jenkins/

Jenkins系统设置

菜单路径:系统管理—管理Jenkins—系统设置

  1. 设置主目录
  2. 设置Maven。注意要预先在操作系统中安装好Maven3.1.1版本。注意不能用更Maven3.2.X版本,否则运行会出错。
  3. 设置SubVersion
  4. 设置邮件通知。要预先在操作系统中搭建好邮件服务器

Jenkins插件

菜单路径:系统管理—管理Jenkins—管理插件。

安装以下插件:

1.      Maven Project Plugin

2.      Subversion Plugin

3.      Checkstyle Plugin

4.      Findbugs Plugin

5.      PMD Plugin

6.      Warnings Plugin

Maven工程配置

Jenkins支持两种工程:FreeStyle工程和Maven工程。如果代码是用Maven构造的,可以直接建立Maven工程。

新建一个Maven工程后,需要设置以下条目:

  1. 设置svn代码的下载路径
  2. 设置自动构造的日期
  3. 设置pom文件的路径
  4. 设置mvn命令的参数
  5. 设置post-build的动作
作者:jiangfuqiang 发表于2014-8-11 19:45:54 原文链接
阅读:70 评论:0 查看评论

【译】编写高性能JavaScript

$
0
0

   英文链接:Writing Fast, Memory-Efficient JavaScript

  很多JavaScript引擎,如Google的 V8引擎(被Chrome和Node所用),是专门为需要 快速执行的大型JavaScript应用所设计的。如果你是一个开发者,并且关心内存使用情况与页面性能,你应该了解用户浏览器中的JavaScript引擎是如何运作的。无论是V8, SpiderMonkey的(Firefox)的 Carakan(Opera), Chakra(IE)或其他引擎,这样做可以帮助你 更好地优化你的应用程序。这并不是说应该专门为某一浏览器或引擎做优化,千万别这么做。

  但是,你应该问自己几个问题:

  • 在我的代码里,是否可以使代码更高效一些
  • 主流的JavaScript引擎都做了哪些优化
  • 什么是引擎无法优化的,垃圾回收器(GC)是否能回收我所期望的东西

加载快速的网站就像是一辆快速的跑车,需要用到特别定制的零件. 图片来源:  dHybridcars.

  编写高性能代码时有一些常见的陷阱,在这篇文章中,我们将展示一些经过验证的、更好的编写代码方式。

   那么,JavaScript在V8里是如何工作的?

  如果你对JS引擎没有较深的了解,开发一个大型Web应用也没啥问题,就好比会开车的人也只是看过引擎盖而没有看过车盖内的引擎一样。鉴于Chrome是我的浏览器首选,所以谈一下它的JavaScript引擎。V8是由以下几个核心部分组成:

  • 一个基本的编译器,它会在代码执行前解析JavaScript代码并生成本地机器码,而不是执行字节码或简单地解释它。这些代码最开始并不是高度优化的。
  • V8将对象构建为 对象模型。在JavaScript中对象表现为关联数组,但是在V8中对象被看作是隐藏的类,一个为了优化查询的内部类型系统。
  • 运行时分析器监视正在运行的系统,并标识了“hot”的函数(例如花费很长时间运行的代码)。
  • 优化编译器重新编译和优化那些被运行时分析器标识为“hot”的代码,并进行“内联”等优化(例如用被调用者的主体替换函数调用的位置)。
  • V8支持 去优化,这意味着优化编译器如果发现对于代码优化的假设过于乐观,它会舍弃优化过的代码。
  • V8有个 垃圾收集器,了解它是如何工作的和优化JavaScript一样重要。

   垃圾回收

  垃圾回收是 内存管理的一种形式,其实就是一个收集器的概念,尝试回收不再被使用的对象所占用的内存。在JavaScript这种垃圾回收语言中,应用程序中仍在被引用的对象不会被清除。

  手动消除对象引用在大多数情况下是没有必要的。通过简单地把变量放在需要它们的地方(理想情况下,尽可能是局部作用域,即它们被使用的函数里而不是函数外层),一切将运作地很好。

垃圾回收器尝试回收内存. 图片来源:  Valtteri Mäki.

  在JavaScript中,是不可能强制进行垃圾回收的。你不应该这么做,因为垃圾收集过程是由运行时控制的,它知道什么是最好的清理时机。

   “消除引用”的误解

  网上有许多关于JavaScript内存回收的讨论都谈到delete这个关键字,虽然它可以被用来删除对象(map)中的属性(key),但有部分开发者认为它可以用来强制“消除引用”。建议尽可能避免使用delete,在下面的例子中 delete o.x 的弊大于利,因为它改变了o的隐藏类,并使它成为一个"慢对象"。

var o = { x: 1 };
delete o.x; // true
o.x; // undefined

  你会很容易地在流行的JS库中找到引用删除——这是具有语言目的性的。这里需要注意的是避免在运行时修改”hot”对象的结构。JavaScript引擎可以检测出这种“hot”的对象,并尝试对其进行优化。如果对象在生命周期中其结构没有较大的改变,引擎将会更容易优化对象,而delete操作实际上会触发这种较大的结构改变,因此不利于引擎的优化。

  对于null是如何工作也是有误解的。将一个对象引用设置为null,并没有使对象变“空”,只是将它的引用设置为空而已。使用o.x= null比使用delete会更好些,但可能也不是很必要。

var o = { x: 1 };
o = null;
o; // null
o.x // TypeError

  如果此引用是当前对象的最后引用,那么该对象将被作为垃圾回收。如果此引用不是当前对象的最后引用,则该对象是可访问的且不会被垃圾回收。

  另外需要注意的是,全局变量在页面的生命周期里是不被垃圾回收器清理的。无论页面打开多久,JavaScript运行时全局对象作用域中的变量会一直存在。

var myGlobalNamespace = {};

  全局对象只会在刷新页面、导航到其他页面、关闭标签页或退出浏览器时才会被清理。函数作用域的变量将在超出作用域时被清理,即退出函数时,已经没有任何引用,这样的变量就被清理了。

   经验法则

  为了使垃圾回收器尽早收集尽可能多的对象, 不要hold着不再使用的对象。这里有几件事需要记住:

  • 正如前面提到的,在合适的范围内使用变量是手动消除引用的更好选择。即一个变量只在一个函数作用域中使用,就不要在全局作用域声明它。这意味着更干净省心的代码。
  • 确保解绑那些不再需要的事件监听器,尤其是那些即将被销毁的DOM对象所绑定的事件监听器。
  • 如果使用的数据缓存在本地,确保清理一下缓存或使用老化机制,以避免大量不被重用的数据被存储。

   函数

  接下来,我们谈谈函数。正如我们已经说过,垃圾收集的工作原理,是通过回收不再是访问的内存块(对象)。为了更好地说明这一点,这里有一些例子。

function foo() {
var bar = new LargeObject();
bar.someCall();
}

  当foo返回时,bar指向的对象将会被垃圾收集器自动回收,因为它已没有任何存在的引用了。

  对比一下:

function foo() {
var bar = new LargeObject();
bar.someCall();
return bar;
}

// somewhere else
var b = foo();

  现在我们有一个引用指向bar对象,这样bar对象的生存周期就从foo的调用一直持续到调用者指定别的变量b(或b超出范围)。

   闭包(CLOSURES)

  当你看到一个函数,返回一个内部函数,该内部函数将获得范围外的访问权,即使在外部函数执行之后。这是一个基本的 闭包—— 可以在特定的上下文中设置的变量的表达式。例如:

function sum (x) {
function sumIt(y) {
return x + y;
};
return sumIt;
}

// Usage
var sumA = sum(4);
var sumB = sumA(3);
console.log(sumB); // Returns 7

  在sum调用上下文中生成的函数对象(sumIt)是无法被回收的,它被全局变量(sumA)所引用,并且可以通过sumA(n)调用。

  让我们来看看另外一个例子,这里我们可以访问变量largeStr吗?

var a = function () {
var largeStr = new Array(1000000).join('x');
return function () {
return largeStr;
};
}();

  是的,我们可以通过a()访问largeStr,所以它没有被回收。下面这个呢?

var a = function () {
var smallStr = 'x';
var largeStr = new Array(1000000).join('x');
return function (n) {
return smallStr;
};
}();

  我们不能再访问largeStr了,它已经是垃圾回收候选人了。【译者注:因为largeStr已不存在外部引用了】

   定时器

  最糟的内存泄漏地方之一是在循环中,或者在setTimeout()/ setInterval()中,但这是相当常见的。思考下面的例子:

var myObj = {
callMeMaybe: function () {
var myRef = this;
var val = setTimeout(function () {
console.log('Time is running out!');
myRef.callMeMaybe();
}, 1000);
}
};

  如果我们运行myObj.callMeMaybe();来启动定时器,可以看到控制台每秒打印出“Time is running out!”。如果接着运行 myObj = null,定时器依旧处于激活状态。为了能够持续执行,闭包将myObj传递给setTimeout,这样myObj是无法被回收的。相反,它引用到myObj的因为它捕获了myRef。这跟我们为了保持引用将闭包传给其他的函数是一样的。

  同样值得牢记的是,setTimeout/setInterval调用(如函数)中的引用,将需要执行和完成,才可以被垃圾收集。

   当心性能陷阱

  永远不要优化代码,直到你真正需要。现在经常可以看到一些基准测试,显示N比M在V8中更为优化,但是在模块代码或应用中测试一下会发现,这些优化真正的效果比你期望的要小的多。

做的过多还不如什么都不做. 图片来源:  Tim Sheerman-Chase.

  比如我们想要创建这样一个模块:

  • 需要一个本地的数据源包含数字ID
  • 绘制包含这些数据的表格
  • 添加事件处理程序,当用户点击的任何单元格时切换单元格的css class

  这个问题有几个不同的因素,虽然也很容易解决。我们如何存储数据,如何高效地绘制表格并且append到DOM中,如何更优地处理表格事件?

  面对这些问题最开始(天真)的做法是使用对象存储数据并放入数组中,使用jQuery遍历数据绘制表格并append到DOM中,最后使用事件绑定我们期望地点击行为。

   注意:这不是你应该做的

var moduleA = function () {

return {

data: dataArrayObject,

init: function () {
this.addTable();
this.addEvents();
},

addTable: function () {

for (var i = 0; i < rows; i++) {
$tr = $('<tr></tr>');
for (var j = 0; j < this.data.length; j++) {
$tr.append('<td>' + this.data[j]['id'] + '</td>');
}
$tr.appendTo($tbody);
}

},
addEvents: function () {
$('table td').on('click', function () {
$(this).toggleClass('active');
});
}

};
}();

  这段代码简单有效地完成了任务。

  但在这种情况下,我们遍历的数据只是本应该简单地存放在数组中的数字型属性ID。有趣的是,直接使用DocumentFragment和本地DOM方法比使用jQuery(以这种方式)来生成表格是更优的选择,当然,事件代理比单独绑定每个td具有更高的性能。

  要注意虽然jQuery在内部使用DocumentFragment,但是在我们的例子中,代码在循环内调用append并且这些调用涉及到一些其他的小知识,因此在这里起到的优化作用不大。希望这不会是一个痛点,但请务必进行基准测试,以确保自己代码ok。

  对于我们的例子,上述的做法带来了(期望的)性能提升。事件代理对简单的绑定是一种改进,可选的DocumentFragment也起到了助推作用。

var moduleD = function () {

return {

data: dataArray,

init: function () {
this.addTable();
this.addEvents();
},
addTable: function () {
var td, tr;
var frag = document.createDocumentFragment();
var frag2 = document.createDocumentFragment();

for (var i = 0; i < rows; i++) {
tr = document.createElement('tr');
for (var j = 0; j < this.data.length; j++) {
td = document.createElement('td');
td.appendChild(document.createTextNode(this.data[j]));

frag2.appendChild(td);
}
tr.appendChild(frag2);
frag.appendChild(tr);
}
tbody.appendChild(frag);
},
addEvents: function () {
$('table').on('click', 'td', function () {
$(this).toggleClass('active');
});
}

};

}();

  接下来看看其他提升性能的方式。你也许曾经在哪读到过使用原型模式比模块模式更优,或听说过使用JS模版框架性能更好。有时的确如此,不过使用它们其实是为了代码更具可读性。对了,还有预编译!让我们看看在实践中表现的如何?

moduleG = function () {};

moduleG.prototype.data = dataArray;
moduleG.prototype.init = function () {
this.addTable();
this.addEvents();
};
moduleG.prototype.addTable = function () {
var template = _.template($('#template').text());
var html = template({'data' : this.data});
$tbody.append(html);
};
moduleG.prototype.addEvents = function () {
$('table').on('click', 'td', function () {
$(this).toggleClass('active');
});
};

var modG = new moduleG();

  事实证明,在这种情况下的带来的性能提升可以忽略不计。 模板和原型的选择并没有真正提供更多的东西。也就是说,性能并不是开发者使用它们的原因,给代码带来的可读性、继承模型和可维护性才是真正的原因。

  更复杂的问题包括 高效地在canvas上绘制图片和操作带或不带类型数组的像素数据。

  在将一些方法用在你自己的应用之前,一定要多了解这些方案的基准测试。也许有人还记得 JS模版的shoot-off随后的扩展版。你要搞清楚基准测试不是存在于你看不到的那些虚拟应用,而是应该在你的实际代码中去测试带来的优化。

   V8优化技巧

  详细介绍了每个V8引擎的优化点在本文讨论范围之外,当然这里也有许多值得一提的技巧。记住这些技巧你就能减少那些性能低下的代码了。

  • 特定模式可以使V8摆脱优化的困境,比如说try-catch。欲了解更多有关哪些函数能或不能进行优化,你可以在V8的脚本工具d8中使用–trace-opt file.js命令。
  • 如果你关心速度,尽量使你的函数职责单一,即确保变量(包括属性,数组,函数参数)只使用相同隐藏类包含的对象。举个例子,别这么干:
function add(x, y) {
return x+y;
}

add(1, 2);
add('a','b');
add(my_custom_object, undefined);
  • 不要加载未初始化或已删除的元素。如果这么做也不会出现什么错误,但是这样会使速度变慢。
  • 不要使函数体过大,这样会使得优化更加困难。

  更多内容可以去看Daniel Clifford在Google I/O的分享  Breaking the JavaScript Speed Limit with V8。  Optimizing For V8 — A Series也非常值得一读。

   对象VS数组:我应该用哪个?

  • 如果你想存储一串数字,或者一些相同类型的对象,使用一个数组。
  • 如果你语义上需要的是一堆的对象的属性(不同类型的),使用一个对象和属性。这在内存方面非常高效,速度也相当快。
  • 整数索引的元素,无论存储在一个数组或对象中,都要 比遍历对象的属性快得多
  • 对象的属性比较复杂:它们可以被setter们创建,具有不同的枚举性和可写性。数组中则不具有如此的定制性,而只存在有和无这两种状态。在引擎层面,这允许更多存储结构方面的优化。特别是当数组中存在数字时,例如当你需要容器时,不用定义具有x,y,z属性的类,而只用数组就可以了。

  JavaScript中对象和数组之间只有一个的主要区别,那就是数组神奇的length属性。如果你自己来维护这个属性,那么V8中对象和数组的速度是一样快的。

   使用对象时的技巧

  • 使用一个构造函数来创建对象。这将确保它创建的所有对象具有相同的隐藏类,并有助于避免更改这些类。作为一个额外的好处,它也 略快于Object.create()
  • 你的应用中,对于使用不同类型的对象和其复杂度(在合理的范围内:长原型链往往是有害的,呈现只有一个极少数属性的对象比大对象会快一点)是有没限制的。对于“hot”对象,尽量保持短原型链,并且少属性。

   对象克隆

  对于应用程序开发人员,对象克隆是一个常见的问题。虽然各种基准测试可以证明V8对这个问题处理得很好,但仍要小心。复制大的东西通常是较慢的——不要这么做。JS中的for..in循环尤其糟糕,因为它有着恶魔般的规范,并且无论是在哪个引擎中,都可能永远不会比任何对象快。

  当你一定要在关键性能代码路径上复制对象时,使用数组或一个自定义的“拷贝构造函数”功能明确地复制每个属性。这可能是最快的方式:

function clone(original) {
this.foo = original.foo;
this.bar = original.bar;
}
var copy = new clone(original);

   模块模式中缓存函数

  使用模块模式时缓存函数,可能会导致性能方面的提升。参阅下面的例子,因为它总是创建成员函数的新副本,你看到的变化可能会比较慢。

  另外请注意,使用这种方法明显更优,不仅仅是依靠原型模式(经过jsPerf测试确认)。

使用模块模式或原型模式时的性能提升

  这是一个 原型模式与模块模式的性能对比测试

// Prototypal pattern
Klass1 = function () {}
Klass1.prototype.foo = function () {
log('foo');
}
Klass1.prototype.bar = function () {
log('bar');
}

// Module pattern
Klass2 = function () {
var foo = function () {
log('foo');
},
bar = function () {
log('bar');
};

return {
foo: foo,
bar: bar
}
}

// Module pattern with cached functions
var FooFunction = function () {
log('foo');
};
var BarFunction = function () {
log('bar');
};

Klass3 = function () {
return {
foo: FooFunction,
bar: BarFunction
}
}

// Iteration tests

// Prototypal
var i = 1000,
objs = [];
while (i--) {
var o = new Klass1()
objs.push(new Klass1());
o.bar;
o.foo;
}

// Module pattern
var i = 1000,
objs = [];
while (i--) {
var o = Klass2()
objs.push(Klass2());
o.bar;
o.foo;
}

// Module pattern with cached functions
var i = 1000,
objs = [];
while (i--) {
var o = Klass3()
objs.push(Klass3());
o.bar;
o.foo;
}
// See the test for full details

   使用数组时的技巧

  接下来说说数组相关的技巧。在一般情况下, 不要删除数组元素,这样将使数组过渡到较慢的内部表示。当索引变得稀疏,V8将会使元素转为更慢的字典模式。

   数组字面量

  数组字面量非常有用,它可以暗示VM数组的大小和类型。它通常用在体积不大的数组中。

// Here V8 can see that you want a 4-element array containing numbers:
var a = [1, 2, 3, 4];

// Don't do this:
a = []; // Here V8 knows nothing about the array
for(var i = 1; i <= 4; i++) {
a.push(i);
}

   存储单一类型VS多类型

  将混合类型(比如数字、字符串、undefined、true/false)的数据存在数组中绝不是一个好想法。例如var arr = [1, “1”, undefined, true, “true”]

   类型推断的性能测试

  正如我们所看到的结果,整数的数组是最快的。

   稀疏数组与满数组

  当你使用稀疏数组时,要注意访问元素将远远慢于满数组。因为V8不会分配一整块空间给只用到部分空间的数组。取而代之的是,它被管理在字典中,既节约了空间,但花费访问的时间。

   稀疏数组与满数组的测试

   预分配空间VS动态分配

  不要预分配大数组(如大于64K的元素),其最大的大小,而应该动态分配。在我们这篇文章的性能测试之前,请记住这只适用部分JavaScript引擎。

空字面量与预分配数组在不同的浏览器进行测试

  Nitro (Safari)对预分配的数组更有利。而在其他引擎(V8,SpiderMonkey)中,预先分配并不是高效的。

   预分配数组测试

// Empty array
var arr = [];
for (var i = 0; i < 1000000; i++) {
arr[i] = i;
}

// Pre-allocated array
var arr = new Array(1000000);
for (var i = 0; i < 1000000; i++) {
arr[i] = i;
}

   优化你的应用

  在Web应用的世界中,速度就是一切。没有用户希望用一个要花几秒钟计算某列总数或花几分钟汇总信息的表格应用。这是为什么你要在代码中压榨每一点性能的重要原因。

图片来源:  Per Olof Forsberg.

  理解和提高应用程序的性能是非常有用的同时,它也是困难的。我们推荐以下的步骤来解决性能的痛点:

  • 测量:在您的应用程序中找到慢的地方(约45%)
  • 理解:找出实际的问题是什么(约45%)
  • 修复它! (约10%)

  下面推荐的一些工具和技术可以协助你。

   基准化(BENCHMARKING)

  有很多方式来运行JavaScript代码片段的基准测试其性能——一般的假设是,基准简单地比较两个时间戳。这中模式被 jsPerf团队指出,并在 SunSpiderKraken的基准套件中使用:

var totalTime,
start = new Date,
iterations = 1000;
while (iterations--) {
// Code snippet goes here
}
// totalTime → the number of milliseconds taken
// to execute the code snippet 1000 times
totalTime = new Date - start;

  在这里,要测试的代码被放置在一个循环中,并运行一个设定的次数(例如6次)。在此之后,开始日期减去结束日期,就得出在循环中执行操作所花费的时间。

  然而,这种基准测试做的事情过于简单了,特别是如果你想运行在多个浏览器和环境的基准。垃圾收集器本身对结果是有一定影响的。即使你使用window.performance这样的解决方案,也必须考虑到这些缺陷。

  不管你是否只运行基准部分的代码,编写一个测试套件或编码基准库,JavaScript基准其实比你想象的更多。如需更详细的指南基准,我强烈建议你阅读由Mathias Bynens和John-David Dalton提供的 Javascript基准测试

   分析(PROFILING)

  Chrome开发者工具为 JavaScript分析有很好的支持。可以使用此功能检测哪些函数占用了大部分时间,这样你就可以去优化它们。这很重要,即使是代码很小的改变会对整体表现产生重要的影响。

Chrome开发者工具的分析面板

  分析过程开始获取代码性能基线,然后以时间线的形式体现。这将告诉我们代码需要多长时间运行。“Profiles”选项卡给了我们一个更好的视角来了解应用程序中发生了什么。JavaScript CPU分析文件展示了多少CPU时间被用于我们的代码,CSS选择器分析文件展示了多少时间花费在处理选择器上,堆快照显示多少内存正被用于我们的对象。

  利用这些工具,我们可以分离、调整和重新分析来衡量我们的功能或操作性能优化是否真的起到了效果。

“Profile”选项卡展示了代码性能信息。

  一个很好的分析介绍,阅读Zack Grossbart的  JavaScript Profiling With The Chrome Developer Tools

  提示:在理想情况下,若想确保你的分析并未受到已安装的应用程序或扩展的任何影响,可以使用 --user-data-dir <empty_directory>标志来启动Chrome。在大多数情况下,这种方法优化测试应该是足够的,但也需要你更多的时间。这是V8标志能有所帮助的。

   避免内存泄漏——3快照技术

  在谷歌内部,Chrome开发者工具被Gmail等团队大量使用,用来帮助发现和排除内存泄漏。

Chrome开发者工具中的内存统计

  内存统计出我们团队所关心的私有内存使用、JavaScript堆的大小、DOM节点数量、存储清理、事件监听计数器和垃圾收集器正要回收的东西。推荐阅读Loreena Lee的 “3快照”技术。该技术的要点是,在你的应用程序中记录一些行为,强制垃圾回收,检查DOM节点的数量有没有恢复到预期的基线,然后分析三个堆的快照来确定是否有内存泄漏。

   单页面应用的内存管理

  单页面应用程序(例如AngularJS,Backbone,Ember)的内存管理是非常重要的,它们几乎永远不会刷新页面。这意味着内存泄漏可能相当明显。移动终端上的单页面应用充满了陷阱,因为设备的内存有限,并在长期运行Email客户端或社交网络等应用程序。 能力愈大责任愈重。

  有很多办法解决这个问题。在Backbone中,确保使用dispose()来处理旧视图和引用(目前在 Backbone(Edge)中可用)。这个函数是最近加上的,移除添加到视图“event”对象中的处理函数,以及通过传给view的第三个参数(回调上下文)的model或collection的事件监听器。dispose()也会被视图的remove()调用,处理当元素被移除时的主要清理工作。Ember 等其他的库当检测到元素被移除时,会清理监听器以避免内存泄漏。

  Derick Bailey的一些明智的建议:

与其了解事件与引用是如何工作的,不如遵循的标准规则来管理JavaScript中的内存。如果你想加载数据到的一个存满用户对象的Backbone集合中,你要清空这个集合使它不再占用内存,那必须这个集合的所有引用以及集合内对象的引用。一旦清楚了所用的引用,资源就会被回收。这就是标准的JavaScript垃圾回收规则。

  在文章中,Derick涵盖了许多使用Backbone.js时的常见 内存缺陷,以及如何解决这些问题。

  Felix Geisendörfer的 在Node中调试内存泄漏的教程也值得一读,尤其是当它形成了更广泛SPA堆栈的一部分。

   减少回流(REFLOWS)

  当浏览器重新渲染文档中的元素时需要 重新计算它们的位置和几何形状,我们称之为 回流。回流会阻塞用户在浏览器中的操作,因此理解提升回流时间是非常有帮助的。

回流时间图表

  你应该批量地触发回流或重绘,但是要节制地使用这些方法。尽量不处理DOM也很重要。可以使用 DocumentFragment,一个轻量级的文档对象。你可以把它作为一种方法来提取文档树的一部分,或创建一个新的文档“片段”。与其不断地添加DOM节点,不如使用文档片段后只执行一次DOM插入操作,以避免过多的回流。

  例如,我们写一个函数给一个元素添加20个div。如果只是简单地每次append一个div到元素中,这会触发20次回流。

function addDivs(element) {
var div;
for (var i = 0; i < 20; i ++) {
div = document.createElement('div');
div.innerHTML = 'Heya!';
element.appendChild(div);
}
}

  要解决这个问题,可以使用DocumentFragment来代替,我们可以每次添加一个新的div到里面。完成后将DocumentFragment添加到DOM中只会触发一次回流。

function addDivs(element) {
var div;
// Creates a new empty DocumentFragment.
var fragment = document.createDocumentFragment();
for (var i = 0; i < 20; i ++) {
div = document.createElement('a');
div.innerHTML = 'Heya!';
fragment.appendChild(div);
}
element.appendChild(fragment);
}

  可以参阅  Make the Web FasterJavaScript Memory Optimization 和  Finding Memory Leaks

   JS内存泄漏探测器

  为了帮助发现JavaScript内存泄漏,谷歌的开发人员((Marja Hölttä和Jochen Eisinger)开发了一种工具,它与Chrome开发人员工具结合使用,检索堆的快照并检测出是什么对象导致了内存泄漏。

一个JavaScript内存泄漏检测工具

  有完整的文章介绍了 如何使用这个工具,建议你自己到 内存泄漏探测器项目页面看看。

  如果你想知道为什么这样的工具还没集成到我们的开发工具,其原因有二。它最初是在Closure库中帮助我们捕捉一些特定的内存场景,它更适合作为一个外部工具。

   V8优化调试和垃圾回收的标志位

  Chrome支持直接通过传递一些标志给V8,以获得更详细的引擎优化输出结果。例如,这样可以追踪V8的优化:

"/Applications/Google Chrome/Google Chrome" --js-flags="--trace-opt --trace-deopt"

  Windows用户可以这样运行 chrome.exe –js-flags=”–trace-opt –trace-deopt”

  在开发应用程序时,下面的V8标志都可以使用。

  • trace-opt —— 记录优化函数的名称,并显示跳过的代码,因为优化器不知道如何优化。
  • trace-deopt —— 记录运行时将要“去优化”的代码。
  • trace-gc —— 记录每次的垃圾回收。

  V8的处理脚本用*(星号)标识优化过的函数,用~(波浪号)表示未优化的函数。

  如果你有兴趣了解更多关于V8的标志和V8的内部是如何工作的,强烈建议 阅读Vyacheslav Egorov的 excellent post on V8 internals

   HIGH-RESOLUTION TIME 和 NAVIGATION TIMING API

   高精度时间(HRT)是一个提供不受系统时间和用户调整影响的亚毫秒级高精度时间接口,可以把它当做是比 new Date 和 Date.now()更精准的度量方法。这对我们编写基准测试帮助很大。

高精度时间(HRT)提供了当前亚毫秒级的时间精度

  目前HRT在Chrome(稳定版)中是以window.performance.webkitNow()方式使用,但在Chrome Canary中前缀被丢弃了,这使得它可以通过window.performance.now()方式调用。Paul Irish在HTML5Rocks上了 关于HRT更多内容的文章。

  现在我们知道当前的精准时间,那有可以准确测量页面性能的API吗?好吧,现在有个 Navigation Timing API可以使用,这个API提供了一种简单的方式,来获取网页在加载呈现给用户时,精确和详细的时间测量记录。可以在console中使用window.performance.timing来获取时间信息:

显示在控制台中的时间信息

  我们可以从上面的数据获取很多有用的信息,例如网络延时为responseEnd – fetchStart,页面加载时间为loadEventEnd – responseEnd,处理导航和页面加载的时间为loadEventEnd – navigationStart。

  正如你所看到的,perfomance.memory的属性也能显示JavaScript的内存数据使用情况,如总的堆大小。

  更多Navigation Timing API的细节,阅读 Sam Dutton的  Measuring Page Load Speed With Navigation Timing

   ABOUT:MEMORY 和 ABOUT:TRACING

  Chrome中的about:tracing提供了浏览器的性能视图,记录了Chrome的所有线程、tab页和进程。

About:Tracing提供了浏览器的性能视图

  这个工具的真正用处是允许你捕获Chrome的运行数据,这样你就可以适当地调整JavaScript执行,或优化资源加载。

  Lilli Thompson有一篇 写给游戏开发者的使用about:tracing分析WebGL游戏的文章,同时也适合JavaScript的开发者。

  在Chrome的导航栏里可以输入about:memory,同样十分实用,可以获得每个tab页的内存使用情况,对定位内存泄漏很有帮助。

   总结

  我们看到, JavaScript的世界中有很多隐藏的陷阱,且并没有提升性能的银弹。只有把一些优化方案综合使用到(现实世界)测试环境,才能获得最大的性能收益。即便如此,了解引擎是如何解释和优化代码,可以帮助你调整应用程序。

   测量,理解,修复。不断重复这个过程。

图片来源:  Sally Hunter

  谨记关注优化,但为了便利可以舍弃一些很小的优化。例如,有些开发者选择.forEach和Object.keys代替for和for..in循环,尽管这会更慢但使用更方便。要保证清醒的头脑,知道什么优化是需要的,什么优化是不需要的。

  同时注意,虽然JavaScript引擎越来越快,但下一个真正的瓶颈是DOM。回流和重绘的减少也是重要的,所以必要时再去动DOM。还有就是要关注网络,HTTP请求是珍贵的,特别是移动终端上,因此要使用HTTP的缓存去减少资源的加载。

  记住这几点可以保证你获取了本文的大部分信息,希望对你有所帮助!

Sql Server 高频,高并发访问中的键查找死锁解析 - shanks_gao

$
0
0

死锁对于DBA或是数据库开发人员而言并不陌生,它的引发多种多样,一般而言,数据库应用的开发者在设计时都会有一定的考量进而尽量避免死锁的产生.但有时因为一些特殊应用场景如高频查询,高并发查询下由于数据库设计的潜在问题,一些不易捕捉的死锁可能出现从而影响业务.这里为大家介绍由于设计问题引起的键查找死锁及相关的解决办法.

这里我们在测试的同时开启trace profiler跟踪死锁视图(locks:deadlock graph).(当然也可以开启跟踪标记,或者应用扩展事件(xevents)等捕捉死锁)

创建测试对象code

create table testklup
(
clskey int not null,
nlskey int not null,
cont1 int not null,
cont2 char(3000)
)

create unique clustered index inx_cls on testklup(clskey)

create unique nonclustered index inx_nlcs on testklup(nlskey) include(cont1)

insert into testklup select 1,1,100,'aaa'
insert into testklup select 2,2,200,'bbb'
insert into testklup select 3,3,300,'ccc'

开启会话1 模拟高频update操作

----模拟高频update操作
declare @i int
set @i=100
while 1=1
begin
update testklup set cont1=@i
where clskey=1
set @i=@i+1
end

开启会话2 模拟高频select操作

----模拟高频select操作
declare @cont2 char(3000)
while 1=1
begin
select @cont2=cont2 from testklup where nlskey=1
end

此时开启会话2执行一小段时间时我们就可以看到类似错误信息:图1-1

                                                     图1-1

 

而在我们开启的跟踪中捕捉到了如下的死锁图.图1-2

                                                                              图1-2

 

死锁分析:可以看出由于读进程(108)请求写进程(79)持有的X锁被阻塞的同时,写进程(79)又申请读进程(108)锁持有的S锁.读执行计划图1-3,写执行计划图1-4

(由于在默认隔离级别下(读提交)读申请S锁只是瞬间过程,读完立即释放,不会等待事务完成),所以在并发,执行频率不高的情形下不易出现.但我们模拟的高频情况使得S锁获得频率非常高,此时就出现了仅仅两个会话,一个读,一个写就造成了死锁现象.

 

                                                                            图1-3

                                                                              图1-4

 

死锁原因:读操作中的键查找造成的额外锁(聚集索引)需求

解决方案:在了解了死锁产生的原因后,解决起来就比较简单了.

我们可以从以下几个方面入手.

a 消除额外的键查找锁需的锁

b 读操作时取消获取锁

a.1我们可以创建覆盖索引使select语句中的查询列包含在指定索引中

 

CREATE NONCLUSTERED INDEX [inx_nlskey_incont2] ON [dbo].[testklup]
([nlskey] ASC) INCLUDE ( [cont2])

 

a.2 根据查询需求,分布执行,通过聚集索引获取查询列,避免键查找.

declare @cont2 char(3000)
declare @clskey int
while 1=1
begin
select @clskey=clskey from testklup where nlskey=1

select @cont2=cont2 from testklup where clskey=@clskey
end

 

b 通过改变隔离级别,使用乐观并发模式,读操作时源行无需锁

declare @cont2 char(3000)
while 1=1
begin
select @cont2=cont2 from testklup with(nolock) where nlskey=1
end

 

结束语.我们在解决问题时,最好弄清问题的本质原因,通过问题点寻找出适合自己的环境的解决方案再实施.


本文链接: Sql Server 高频,高并发访问中的键查找死锁解析,转载请注明。

图片主色获取脚本rgbaster.js小介绍小使用

$
0
0

by zhangxinxu from http://www.zhangxinxu.com
本文地址: http://www.zhangxinxu.com/wordpress/?p=4296

一、我想写一篇长篇小说

我想写一篇长篇小说,每天上下班地铁的几个小时写写。不过还没完全构思好。据说,人在实现伟大的计划之前最好不要跟别人讲,等大米差不多煮熟的时候,再普照四方。但是呢,一直憋在心里也慌得很,实在忍不住了,这里一吐为快,反正是个小嘎吱角的地方,吐个火星(星星之火)没人发现的,(*^__^*) 嘻嘻……

我琢磨着,每周写一段,像周刊一样,写个10年,恩,差不多了。说不定会火,哈哈!!

二、rgbaster.js小介绍

rgbaster.js是段小脚本,可以获得(具有访问权限的)图片的主色,次色等,然后,发挥我们的创意,实现一些精彩的Web交互效果。

rgbaster.js的Github项目地址是: https://github.com/briangonzalez/rgbaster.js

如何使用?
如何让妹子为你下厨?首先,你得有一个厨房。同样的,要使用 rgbaster.js,首先,你得先调用之:

<script src="http://rawgit.com/briangonzalez/rgbaster.js/master/rgbaster.js"></script>

然后,你得有个菜谱,知道该如何对妹子下手:

var img = document.getElementById('image');
// 或者
var img = 'http://example.com/path-to-image.jpg'

RGBaster.colors(img, {
  success: function(payload) {
    // payload.dominant是主色,RGB形式表示
    // payload.secondary是次色,RGB形式表示
    // payload.palette是调色板,含多个主要颜色,数组
    console.log(payload.dominant);
    console.log(payload.secondary);
    console.log(payload.palette);
  }
});

看来,这个妹子很好下手啊!

就一个方法, RGBaster.colors就可以了,第1个参数 img可以是图片DOM也可以是图片的URL地址。后面就是可选参数,说到可选参数,不只 success这一个,还有下面这几位兄弟:

RGBaster.colors(img, {
  paletteSize: 30, // 调色板大小
  exclude: [ 'rgb(255,255,255)' ],  // 不包括白色
  success: function(payload){
    // 包含一些颜色信息(payload)的回调
  }
})

paletteSize30, 则 payload.palette就是个包含前 30主要颜色值的数组。
exclude表示数组中的颜色不参与,剔除。例如,一个小龙女舞剑的图片,我们可能需要的是其舞剑的背景。但是,由于小龙女长年古墓,缺少阳光,面色白皙,加上总是一袭白衣,这大大的白色虽是主色,但却我们想要的,于是需要剔除。
一袭白衣、脸色苍白的小龙女

success就是我们下厨的主要内容了,回调中的 payload参数,包含,主色、次色以及调色板信息,绝对可以满足我们绝大多数的需求。

三、rgbaster.js小使用

下面看看妹子是如何下厨的。

您可以狠狠地点击这里: rgbaster.js与slide图片背景变变变demo

点击下面的四个圆点按钮走起:
4个圆点按钮

可以看到,图片slide的同时,容器的背景色也随着一起改变,而容器的背景色就是美女图片的主色。

截个图让大家感受下:
demo截图某效果

相关JS代码为:

$("#index a").powerSwitch({
    animation: "translate",
    classAdd: "active",
    onSwitch: function(target) {
        RGBaster.colors(target.find("img").get(0), {
          exclude: [ 'rgb(255,255,255)', 'rgb(0,0,0)' ],
          success: function(payload) {
            // 容器的背景色变成图片的主色 - payload.dominant
            $("#box").css("background-color", payload.dominant);
          }
        });    
    }    
}).eq(0).trigger("click");

powerSwitch自己的万能切换插件中的方法, onSwitch是slide切换的回调,回调执行的就是 rgbaster.jsRGBaster.colors方法,不考虑黑白两色,让 box的背景色变成图片的主色,哈,很简单吧~

四、rgbaster.js的使用限制

1. 浏览器支持
由于使用Canvas的 getImageData获得图片颜色信息(我之前的 box-shadow图片生成工具也是用的这个), 因此,需要IE9+浏览器支持;同时,使用了XMLHttpRequest2.0, 貌似要IE10+可以畅快使用(据说IE8/IE9可以使用 XDomainRequest对象实现类似功能,未研究,具体不详);

2. 跨域限制
XMLHttpRequest2.0获得图片数据,本地数据自然无障碍,对于跨域的数据,需要设置。宗旨就是,控制 Access-Control-Allow-Origin,例如,你希望你站点的图片只有我站点可以访问,则想办法通过设置,使得请求信息中包含:

Access-Control-Allow-Origin: http://www.zhangxinxu.com

如果是所有站点,则:

Access-Control-Allow-Origin: *

不同的服务器设置是不一样的,大家自行百之谷之。

五、我想写一篇长篇小说

//zxx: 接开头。

面向的群体是,应该是同龄人。走搞笑风格还是严肃风格,应该带有搞笑,主旨但高大上。最好带点热血。恩~~

还有就是………………哎呀呀,好像很难的样子……按照这个节奏,估计还要再构思个半年~~

在此之前,还是多盘出点技术文章吧。

以上~~

本文为原创文章,会经常更新知识点以及修正一些错误,因此转载请保留原出处,方便溯源,避免陈旧错误知识的误导,同时有更好的阅读体验。
本文地址: http://www.zhangxinxu.com/wordpress/?p=4296

(本篇完)

有话要说?点击 这里发表评论。

HDFS HA与QJM[官网整理]

$
0
0

【使用QJM构建HDFS HA架构(2.2+)】

    本文主要介绍HDFS HA特性,以及如何使用QJM(Quorum Journal Manager)特性实现HDFS HA。

 

一、背景

    HDFS集群中只有一个Namenode,这就会引入单点问题;即如果Namenode故障,那么这个集群将不可用,直到Namenode重启或者其他Namenode接入。

    有两种方式会影响集群的整体可用性:

    1、意外的突发事件,比如物理机器crash,集群将不可用,直到管理员重启Namenode。

    2、系统维护,比如软件升级等,需要关闭Namenode,也会导致集群暂时性的失效。

 

    HDFS HA特性即解决这个问题,它通过在集群中同时运行2个(redundant)Namenodes,并让active和passive之间热备(hot standby)。当Active Namenode故障失效后,即可快速故障转移到新的Namenode上(passive Namenode);也可以在计划维护期间,基于管理员发起(administrator-inited)的友好的failover。

 

二、Architecture

    在典型的HA架构中,有两个独立的机器作为Namenode,任何时刻,只有一个Namenode处于Active状态,另一个处于standby状态(passive,备份);Active Namenode用于接收Client端请求,Standy节点作为slave保持集群的状态数据以备快速failover。

 

    为了让Standby Node与Active Node保持同步,这两个Node都与一组称为JNS的互相独立的进程保持通信(Journal Nodes)。当Active Node上更新了namespace,它将记录修改日志发送给JNS的多数派。Standby noes将会从JNS中读取这些edits,并持续关注它们对日志的变更。Standby Node将日志变更应用在自己的namespace中,当failover发生时,Standby将会在提升自己为Active之前,确保能够从JNS中读取所有的edits;即在failover发生之前,Standy持有的namespace应该与Active保持完全同步。

 

    为了支持快速failover,Standby node持有集群中blocks的最新位置是非常必要的。为了达到这一目的,Datanodes上需要同时配置这两个Namenode的地址,同时和它们都建立心跳链接,并把block位置发送给它们。

 

    任何时刻,只有一个Active Namenode是非常重要的,否则将会导致集群操作的混乱,那么两个Namenode将会分别有两种不同的数据状态,可能会导致数据丢失,或者状态异常,这种情况通常称为“split-brain”(脑裂,三节点通讯阻断,即集群中不同的Datanodes却看到了两个Active Namenodes)。对于JNS(Journal Nodes)而言,任何时候只允许一个Namenode作为writer;在failover期间,原来的Standby Node将会接管Active的所有职能,并负责向JNS写入日志记录,这就阻止了其他Namenode基于处于Active状态的问题。

 

 

三、硬件资源

    为了构建HA集群架构,你需要准备如下资源:

    1、Namenode机器:两台配置对等的物理机器,它们分别运行Active和Standby Node。

    2、JouralNode机器:运行JouralNodes的机器。JouralNode守护进程相当的轻量级,它们可以和hadoop的其他进程部署在一起,比如Namenodes、jobTracker、ResourceManager等。不过为了形成多数派(majority),至少需要3个JouralNodes,因为edits操作必须在多数派上写入成功。当然JNS的个数可以 > 3,且通常为奇数(3,5,7),这样可以更好的容错和形成多数派。如果你运行了N个JNS,那么它可以允许(N-1)/2个JNS进程失效并且不影响工作。

 

    此外,在HA集群中,standby namenode还会对namespace进行checkpoint操作(继承Backup Namenode的特性),因此,就不需要在HA集群中运行SecondaryNamenode、CheckpointNode或者BackupNode。事实上,HA架构中运行上述节点,将会出错(不允许)。

 

四、部署

    一) 、配置

    和HDFS Federation类似,HA配置向后兼容,运行只有一个Namenode运行而无需做任何修改。新的配置中,要求集群中所有的Nodes都有相同的配置文件,而不是根据不同的Node设定不同的配置文件。

 

    和HDFS Federation一样,HA集群重用了“nameservice ID”来标识一个HDFS 实例(事实上它可能包含多个HA Namenods);此外,“NameNode ID”概念被添加到HA中,集群中每个Namenode都有一个不同的ID;为了能够让一个配置文件支持所有的Namenodes(适用与Federation环境),那么相关的配置参数都以“nameservice ID”或“Namenode ID”作为后缀。

 

   修改hdfs-site.xml,增加如下几个配置参数,其参数的顺序无关。

    1、dfs.nameservices:nameservice的逻辑名称。可以为任意可读字符串;如果在Federation中使用,那么还应该包含其他的nameservices,以","分割。

<property><name>dfs.nameservices</name><value>hadoop-ha,hadoop-federation</value></property>

 

    2、dfs.ha.namenodes. [nameservice ID]

<property><name>dfs.ha.namenodes.hadoop-ha</name><value>nn1,nn2</value></property>

    其中“hadoop-ha”需要和1)中配置的nameservice ID匹配,此处我们定义“hadoop-ha”下有2个namenode ID。

 

    3、dfs.namenode.rpc-address. [nameservice ID]. [namenode ID]

    

<property><name>dfs.namenode.rpc-address.hadoop-ha.nn1</name><value>machine1.example.com:8020</value></property><property><name>dfs.namenode.rpc-address.hadoop-ha.nn2</name><value>machine2.example.com:8020</value></property>

    其中nameservice ID需要和1)匹配,namenode ID需要和2) 匹配。配置项的值为相应namenode的hostname以及通讯端口号(Client与namenode RPC通讯端口),它和non-ha模式下“dfs.namenode.rpc-address”作用一样。每个namenode ID都需要单独配置。

 

    你可以根据需要,配置“dfs.namenode.servicerpc-address”,格式和上述一致。(SNN,backup节点与Namenode通讯地址)

 

    4、dfs.namenode.http-address.[nameservice ID].[namenode ID]

<property><name>dfs.namenode.http-address.hadoop-ha.nn1</name><value>machine1.example.com:50070</value></property><property><name>dfs.namenode.http-address.hadoop-ha.nn2</name><value>machine2.example.com:50070</value></property>

    各个namenode的HTTP地址。它和non-ha下的"dfs.namenode.http-address"配置作用一样。

 

     5、dfs.namenode.shared.edits.dir:

    

<property><name>dfs.namenode.shared.edits.dir</name><value>qjournal://node1.example.com:8485;node2.example.com:8485;node3.example.com:8485/hadoop-ha</value></property>

    配置JNS组的url地址,Namenodes将会从JNS组中读写edits。这是一个共享存储区,Active Namenode写入,Standby Node读取,每个Namenodeservice必须配置足够多的JNS地址(>=3,多数派),每条的格式为:

    “qjournal://host1:port1;host2:port2;host3:port3/journalId”

    其中journalId需要和上述配置中的“nameserviceID”匹配。

<property><name>dfs.journalnode.rpc-address</name><value>0.0.0.0:8485</value></property><property><name>dfs.journalnode.http-address</name><value>0.0.0.0:8480</value></property>

    此外,我们还需要在相应的JournalNodes上增加上述配置。

 

    6、dfs.client.failover.proxy.provider. [nameservice ID]

    HDFS Client链接Namenode所使用的类,Client可以通过此类来判定哪个Namenode为Alive,并与它保持通信。目前hadoop中唯一的实现类为"ConfiguaredFailoverProxyProvider"。

<property><name>dfs.client.failover.proxy.provider.hadoop-ha</name><value>org.apache.hadoop.hdfs.server.namenode.ha.ConfiguredFailoverProxyProvider</value></property>

 

    7、dfs.ha.fencing.methods:在failover期间用来隔离Active Namenode的脚本或者java 类列表。

    虽然JNS可以确保集群中只有一个Active Node写入edits,这对保护edits一致性很重要,但是在failover期间,有可能Acitive Node仍然存活,Client可能还与其保持连接提供旧的数据服务,我们可以通过此配置,指定shell脚本或者java程序,SSH到Active NameNode然后Kill Namenode进程。它有两种可选值(具体参见官方文档):

    1) sshfence:SSH登录到Active Namenode,并Kill此进程。首先当前机器能够使用SSH登录到远端,前提是已经授权(rsa)。

    2) shell:运行shell指令隔离Active Namenode。

<property><name>dfs.ha.fencing.methods</name><value>shell(/path/to/my/script.sh arg1 arg2 ...)</value></property>

    “()”之间为shell脚本的路径,以及参数列表。

 

    8、fs.defaultFS(core-site.xml):

    在non-ha下,这个参数值为namenode的地址:“hdfs://namenode:8020”;不过在HA架构下,将使用namenservice名称替代

<property><name>fs.defaultFS</name><value>hdfs://hadoop-ha</value></property>

 

    9、dfs.journalnode.edits.dir:

   指定journalNode存储edits文件的本地路径。

 

    最终,上述配置信息,需要在server和Client端同时配置才能有效的适应HA与failover特性。

 

    二)、部署

    上述配置调整完毕后,我们就可以启动journalNodes守护进程,默认的"sbin/start-dfs.sh"脚本会根据"dfs.namenode.shared.edits.dir"配置,在相应的Datanode上启动journalNodes。当然我们可以使用::"bin/hdfs start journalnode"分别在相应的机器上启动。

    一旦JournalNodes启动成功,它们将会从Namenode上同步metadata。

    1、如果你的HDFS集群是新建的,那么需要在每个Namenode上执行"hdfs namenode -format"指令。

    2、如果你的namenodes已经format了,或者是将non-ha转换成ha架构,你应该在将其中一个namenode上的metadata复制到另一台上(dfs.namenode.name.dir目录下的数据),然后在那个没有format的新加入的namenode上执行"hdfs namenode -bootstrapStandby"。运行这个指令需要确保JournalNodes中持有足够多的edits。

    3、如果你将一个non-ha的Namenode(比如backup,其已经formated)切换成HA,你需要首先运行"hdfs -initializeSharedEdits",这个指令将本地Namenode中的edits初始化Journalnodes。

 

    此后,你就可以启动HA Namenodes。可以通过配置指定的HTTP地址(dfs.namenode.https-address)来查看各个Namenode的状态,Active or Standby。

 

    三)、管理员指令

    HA集群启动后,我们可以通过一些指令来管理HDFS集群。“bin/hdfs haadmin -DFSHAAdmin”指令,其可选参数:

    1、-transitionToActive <namenode id>与-transitionToStandbyl <namenode id>:将指定的namenode ID切换为Active或者standby。这个指令并不会触发“fencing method”,所以不常用,我们通常使用"hdfs haadmin -failover"来切换Namenode状态。

    2、-failover [--forcefence] [--foreactive] <serviceId-fist> <serviceId-second>:在两个Namenode之间failover。这个指令会触发将first节点failover到second节点。如果first处于standby,那么只是简单的将second提升为Active。如果first为Active,那么将会友好的将其切换为standby,如果失败,那么fencing methods将会触发直到成功,此后second将会提升为Active。如果fencing method失败,那么second将不会被提升为Active。

    例如:"hdfs haadmin -DFSHAAdmin -failover nn1 nn2"

    3、-getServiceState <serviceId>:获取serviceId的状态,Active还是Standby。链接到指定的namenode上,并获取其当前的状态,打印出“standby”或者“active”。我可以在crontab中使用此命令,用来监测各个Namenode的状况。

    4、-checkHealth <serviceId>:检测指定的namenode的健康状况。

 

五、自动Failover

    上述介绍了如何配置手动failover,在这种模式下,系统不会自动触发failover,即不会将Standby提升为Active,即使Active已经失效。接下来介绍如何实现自动failover。

    一)、组件

    Automatic Failover中,增加了2个新的组件:zookeeper集群,ZKFailoverController进程(简称为ZKFC)。

    Zookeeper是一个高可用的调度服务,可以保存一系列调度数据,当这些数据变更(notify)时可以通知Client,以及监控(montitor)Clients失效,自动failover的实现将依赖于Zookeeper的几个特性:

    1、Failure delection:失效检测,每个Namenode将会和zookeeper建立一个持久session,如果Namenode失效,那么次session将会过期失效,此后Zookeeper将会通知另一个Namenode,然后触发Failover。

    2、Active Namenode election:zookeeper提供了简单的机制来实现Acitve Node选举,如果当前Active失效,Standby将会获取一个特定的排他锁(lock),那么获取(持有)锁的Node接下来将会成为Active。

 

    ZKFailoverControllor(ZKFC)是一个zookeeper客户端,它主要用来监测和管理Namenodes的状态,每个Namenode机器上都会运行一个ZKFC程序,它的职责为:

    1、Health monitoring:ZKFC间歇性的使用health-check指令ping本地的Namenode,Namenode也会及时的反馈自己的health status。如果Namenode失效,或者unhealthy,或者无响应,那么ZKFS将会标记其为“unhealthy”。

    2、Zookeeper session manangement:当本地Nanenode运行良好时,ZKFC将会持有一个zookeeper session,如果本地Namenode为Active,它同时也持有一个“排他锁”(znode);这个lock在zookeeper中为“ephemeral” znode(临时节点),如果session过期,那么次lock所对应的znode也将被删除。(参见zookeeper特性)

    3、Zookeeper-based election:如果本地Namenode运行良好,并且ZKFS没有发现其他的的Namenode持有lock(比如Active失效后,释放了lock),它将尝试获取锁,如果获取成功,即“赢得了选举”,那么此后将会把本地Namenode标记为Active,然后触发Failover:首先,调用fencing method,然后提升本地Namenode 为Active。

 

    具体Failover过程和详细内容,请参见 HDFS-2185

 

    二)、配置

    在Automatic Failover中,需要把一个重要的配置项添加到hdfs-site.xml中。

<property><name>dfs.ha.automatic-failover.enabled</name><value>true</value></property>

    此外还需要在core-site.xml中,增加如下配置:

<property><name>ha.zookeeper.quorum</name><value>zk1.example.com:2181,zk2.example.com:2181,zk3.example.com:2181</value></property>

    上述zookeeper集群为即备,尽可能选择相对平稳的zk集群。

 

    其中"dfs.ha.automatic-failover.enabled"可以为每个nameservice ID分别配置:dfs.ha.automatic-failover.enabled.[nameservice ID]。此外在core-site.xml中还可以配置Zookeeper Client的相关参数,比如sessionTimeout,这些配置项以"ha.zookeeper"开头,其中"dfs.ha."开头的部分配置项可以用来设定fencing method的相关控制。

 

     三)、初始化HA状态

    上述准备工作结束后,我们还需要在zookeeper中初始化HA的状态,通过执行“hdfs zkfc -formatZK”,此命令将会在zookeeker中创建一个znode,用来保存HA或failover的数据。

 

    四)、启动集群

    可以使用"start-dfs.sh"这个便捷的指令,它启动了hdfs所需要的所有守护进程,当然包括ZKFC。也可以使用"hadoop-daemon.sh start zkfc"手动启动ZKFC客户端。

   

    五)、检验Failover

    一旦Automatic Failover集群启动之后,我们需要检测Failover是否符合预期。首先,我们需要通过命令(getServiceState)或者在Namenode的Web UI上查看各个Namenode的状态,确认两个Namenode是否分别处于Active和Standby;此后,你可以手动关闭Active Namenode,比如使用kill -9 <pid num>,在确定Acitve Node失效后,再次检测原来的Standby是否已经提升为Active;不过因为zookeeper session过期判定需要达到sessionTimeout(可配置,ha.zookeeper.session-timeout),这个failover过程可能需要滞后数秒,默认为5秒。

 

    如果没有按照预期failover,那么你需要检测配置文件是否正确,zk服务是否正确。此外,我们还可以使用上述DFSHAAadmin指令多次尝试。

 

六、FAQ

    1、ZKFC和Namenodes守护进程的启动顺序是否重要?

    No,对于指定的Namenode,你可以在其之前或者之后启动ZKFC均可以,ZKFC只是调度Namenode的存活状态,如果不启动ZKFC,此Namenode将无法参与自动failover过程。

    2、是否需要额外的monitoring?

    你需要在Namenode机器上,添加额外的monitor用来监控ZKFC是否运行。在某些情况下,zookeeper集群的故障可能导致ZKFC意外中断,你需要适时的重启ZKFC。此外,还需要监控Zookeeper集群的运行状况,如果Zookeeper集群失效,那么HA集群将无法failover。

    3、如果Zookeeper失效,将会怎么样?

    如果zookeeper集群故障,那么Automatic Failover将不会触发,即使Namenode失效,这也意味着ZKFC无法正常运行。不过,如果Namenodes正常(即使有一个失效),那么HDFS系统将不会受到影响。因为HDFS Client并没有基于zookeeper做任何事情,当zookeeper集群仍需要尽快的恢复以避免当前Active失效而造成的“split-brain”等问题。

    4、是否可以在Namenodes之间指定优先级?

    NO,这是不能支持的。首先启动的Namenode将作为Active,我们只能认为控制Namenode启动的顺序来做到“优先级”。

    5、在Automatic Failover中,手动Failover怎么做?

    和普通的Failover一样,我们总是可以通过"hdfs haadmin -DFSHAAdmin -failover"来实现手动Failover。

 



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


ITeye推荐



免费手机号码归属地API查询接口

$
0
0
免费手机号码归属地API查询接口
 
一、淘宝网API 
   API地址: 
http://tcc.taobao.com/cc/json/mobile_tel_segment.htm?tel=15850781443 
参数: 
tel:手机号码 
返回:JSON 

 

二、拍拍API 
  API地址: 
http://virtual.paipai.com/extinfo/GetMobileProductInfo?mobile=15850781443&amount=10000&callname=getPhoneNumInfoExtCallback 
参数: 
mobile:手机号码 
callname:回调函数 
amount:未知(必须) 
返回:JSON 
 
三、财付通API 
  API地址:
http://life.tenpay.com/cgi-bin/mobile/MobileQueryAttribution.cgi?chgmobile=15850781443 
参数: 
chgmobile:手机号码 
返回:xml 
 
四、百付宝API 
  API地址: 
https://www.baifubao.com/callback?cmd=1059&callback=phone&phone=15850781443 
参数: 
phone:手机号码 
callback:回调函数 
cmd:未知(必须) 
返回:JSON 
 
五、115API 
  API地址: 
http://cz.115.com/?ct=index&ac=get_mobile_local&callback=jsonp1333962541001&mobile=15850781443 
参数: 
mobile:手机号码 
callback:回调函数 
返回:JSON 
六、有道api接口 
  接口地址:
http://www.youdao.com/smartresult-xml/search.s?type=mobile&q=13892101112 
参数说明: 
type : 参数手机归属地固定为mobile 
q : 手机号码 
返回XML格式: 
<?xml version="1.0" encoding="gbk"?> <smartresult> <product type="mobile"> <phonenum>13892101112</phonenum> <location>陕西 延安</location> </product> </smartresult> 
或者 
http://www.youdao.com/smartresult-xml/search.s?jsFlag=true&type=mobile&q=手机号码 
返回JSON格式: 
fYodaoCallBack(1, {‘product':'mobile','phonenum':'13892101112′,'location':'陕西 延安'} , ”);
 


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


ITeye推荐




Nginx 安装 HTTPS 证书

$
0
0

基本步骤可以参考 这篇文章, 但这篇文章有一个致命错误, 就是没有安装 INTERMEDIATE CA, 照样会被浏览器显示证书不可信.

1. 生成 server.key.orig

sudo openssl genrsa -des3 -out server.key.orig 2048

2. 生成 server.csr 和 server.key

sudo openssl req -new -key server.key.orig -out server.csr
sudo openssl rsa -in server.key.orig -out server.key

3. 拿着 server.csr 去证书厂商买证书

4. 买完后, 厂商会给你发两个证书 server.crt 和 server.intermediate.crt

5. 生成最终的 server.chained.crt

cat server.crt server.intermediate.crt > server.chained.crt

6. 配置 nginx

ssl_certificate server.chained.crt;
ssl_certificate_key server.key;

Related posts:

  1. 初学者的 Linux 安装 Nginx PHP fpm 配置教程
  2. Nginx + PHP 配置和启动脚本
  3. lighttpd配置HTTPS(SSL)
  4. Nginx 499 错误码以及 AJAX 调用失败
  5. nginx-push-stream-module 笔记

html5移动web开发实战必读书记

$
0
0

一、配置移动开发环境
1.各种仿真器、模拟器的下载安装
http://www.mobilexweb.com/emulators
https://github.com/h5bp/mobile-boilerplate/wiki/Mobile-Emulators-&-Simulators

2.html5 DTD
<!doctype html>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
safari: 设为屏幕宽度,并根据initial-scale=1.0禁止浏览器缩放

3.帮助html5跨浏览器的库
modernizr
html5shim
innershiv

4.使html5元素在旧版本IE中变成块级元素以及css重置

5.始终使用流式布局fluid layout

6.css媒介查询 media query
@media screen and (min-width: 480px) {...}

@media only screen and (min-width: 320px) {...}

7.用户需求分析工具
google analytics
percentmobile

jQuery mobile的浏览器分级列表:http://jquerymobile.com/gbs/
https://github.com/h5bp/mobile-boilerplate/wiki/Mobile-Matrices

二、移动端的配置和优化
1.提供启动图标(优化对各种浏览器的支持)

<link rel="apple-touch-icon-precomposed" sizes="114x114" href="icons/apple-touch-icon-114x114-precomposed.png"> # iphone4<link rel="apple-touch-icon-precomposed" sizes="72x72" href="icons/apple-touch-icon-72x72-precomposed.png"> # ipad<link rel="apple-touch-icon-precomposed" href="icons/apple-touch-icon-precomposed.png"> # android<link rel="shortcut icon" href="icons/apple-touch-icon.png"> # symbian60

 

文档:关于触摸式图标的一起

2.避免字体被浏览器自动重置
html {
    -webkit-text-size-adjust: none;
}
改进
html {
    -webkit-text-size-adjust: 100%;
    -ms-text-size-adjust: 100%;
    text-size-adjust: 100%;
}

3.使用px

4.浏览器宽度完整解决方案
<meta name="HandheldFriendly" content="true"> # 古老版本浏览器
<meta name="MobileOptimized" content="320"> # 老版本浏览器
<meta name="viewport" content="width=device-width">

5.修复移动版safari的re-flow scale问题
<meta name="viewport" content="width=device-width, maximum-scale=1.0, minimum-scale=1.0"> # 不能缩放了
使用js代码解决不能缩放问题:

var metas = document.getElementByTagName("meta");
var i;
if(navigator.userAgent.match("/iPhone/i)) {
  for(i=0; i<metas.length; i++) {
    if(meta[i].name == "viewport") {
      metas[i].content = "width=device-width, maximum-scale=1.0, minimum-scale=1.0";
    }
  }
  document.addEventListener("gesturestart", gestureStart, false);
}

function gestureStart() {
  for(i=0; i<metas.length; i++) {
    if(meta[i].name == "viewport") {
      metas[i].content = "width=device-width, maximum-scale=1.6, minimum-scale=0.25";
    }
  }
}

 

更好的版本:https://gist.github.com/903131
jquery mobile版本:https://gist.github.com/1183357

6.在浏览器中启动原生应用
查看safari,黑莓,索爱支持哪些链接启动原生应用

7.iphone下全屏
<meta name="apple-mobile-web-app-capable" content="yes"> # 从界面图标启动时,全屏
<meta name="apple-mobile-web-app-status-bar-style" content="black"> # 顶部一个状态栏
<link rel="apple-touch-startup-image" href="img/1/splash.png"> # 预加载界面图片,类似ajax效果

8.防止ios在聚焦时自动缩放,例如填写表单时
<script>
</script>

9.禁用或者限制部分webkit特性
-webkit-touch-callout
-webkit-user-select
-webkit-tap-highlight-color
-webkit-appearance

为狭窄的浏览器添加省略号功能:
.ellipsis {
  text-overflow: ellipsis;
  overflow: hidden;
  white-space: nowrap;
}

三、移动设备的交互方式
1.利用触控来移动页面元素
2.检测盒处理横竖屏切换事件
window.onorientationchange事件
禁止横竖屏对于网友非常困难
3.利用手势旋转页面元素
ongestureend
4.利用滑动创建图库
zepto框架

5.利用手势缩放图片
ongesturechange

四、构建快速响应式移动互联网站点
1.html5标签
header,nav,footer,small,address

2.css3辅助
polyfills库
ultimate css gradient generator # css线性渐变效果编辑器
CSS3 PIE # 兼容IE9

3.响应式
使用modernizr库开检查浏览器对html3和css3的支持
可以用来检查后是否加载respond.min.js
yepnope异步加载

4.检测客户端
.htaccess重定向

5.使用书签冒泡为应用添加桌面快捷方式
mobilebookmark bubble库 from google # 只支持safari

6.构建可伸缩的文本输入框
mobile boilerplate库的helper.js

7.加速按钮反馈
touchstart

8.隐藏浏览器地址栏
MBP.hideUrlBar();

五、移动设备访问
1.获取位置信息
经度、纬度、当前位置的精确程度
navigator.geolocation.getCurrentPosition()

2.跨浏览器定位
geo-location-javascript库
延伸:YQL Geo库

手势缩放:
<meta name="viewport" content="initial-scale=1.0, user-scalable=no" />

3.实时显示地理位置
watchPosition

4.DeviceOrientation事件 # 适用于ios
包括设备移动事件和横竖屏切换事件

5.使用foursquare定位
Marelle基于jquery和coffeescript
https://praized.github.com/marelle/
包括两个例子:登录和签到

foursquare接口列表
https://developer.foursquare.com/docs/libraries.html

六、移动富媒体
1.移动设备上播放音频
<audio>
不支持html5的浏览器使用polyfills解决

2.移动设备上播放视频
<video>
不支持html5的使用<boject>

http://diveintohtml5.info/video.html

3.使用离线缓存
使用.appcache

4.使用网络存储
web storage # 浏览器支持度最高
html5的indexed database api和web sql database
jqueryoffine库

5.使用web workers
javascript多线程的补充

6.使用session和history api构建类Flash导航效果

七、移动设备调试
1.使用opera dragonfly远程调试
2.使用weinre远程调试

3.移动设备上使用firebug
4.使用js console远程调试

5.配置移动safari调试器

八、服务器端性能调优
1.防止移动设备转码
.htaccess配置

2.添加移动设备支持的MIME类型
.htaccess配置 # 针对blackberry和Symbian等

3.正确显示cache manifest # 主要用来做离线应用存储功能,但是扩展名不能被服务器识别
.htccess配置

4.在头文件中设置未来过期时间
.htccess配置

5.使用gzip压缩
.htaccess配置

6.移除etags
.htaccess配置

九、移动性能测试
1.使用blaze的移动设备速度测试

2.在线分析移动页面速度
google page speed

3.pcap网站性能分析

4.移动版http archive

5.使用jdrop存储性能数据

十、拥抱移动互联网特性
1.window.onerror
2.使用ecmascript5中的新方法
3.html5中的新输入类型
date、datetime、month、time、range
4.HTML中内嵌svg
5.position:fixed
6.overflow:scroll # ios5+
-webkit-overflow-scrolling: touch



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


ITeye推荐



Android手机真的那么容易中毒吗?

$
0
0

  日前一款被称为“超级手机病毒”的木马再一次引爆了人们对Android系统安全性的担忧。而在这个二维码多的都闪瞎眼的时代,什么扫一下二维码支付宝钱就被转走了、扫一下二维码支付宝钱就立马被盗的消息比比皆是。

  通过N多个例子,咱说下扫二维码支付宝钱被盗的流程:

  扫描二维码→程序识别结果为链接→程序询问用户是否打开→打开链接后自动下载(自动下载是因为链接后缀为.apk)→下载完成→弹出APP安装界面→询问用户是否安装→用户选择安装→安装完成→点击打开或点击APP图标启动→手机中毒。

  木马运行读取用户手机号码并发送到木马制作者→木马作者用手机号进行支付宝登陆→选择忘记密码→发送验证码到用户手机→木马拦截掉验证码短信并发送到木马制作者→密码修改成功→登陆支付宝→进行转账操作→选择忘记支付密码→发送验证码到用户手机→木马拦截掉验证码短信并发送到木马制作者→密码修改成功→转账成功。

  步骤很多?其实真操作起来也就分分钟的事儿,红色标注的均为用户的操作,好几个步骤呢?谁把自己卖了?还是用户自己..

  在此,我们并不去谈涉及到Android系统本身漏洞的问题,毕竟大部分病毒只是简单的利用用户安全意识低这方面。

  所以说到底还是用户自己的安全意识低了,所以各大媒体各种报道如上图那种资讯还是很有用的,提醒用户一定得谨慎扫码。

  不过支付宝现在已经在修改密码环节增加了需要输入身份证号的验证,那么你可能会想,木马作者又怎么知道用户的身份证号的呢?方法很多,比如那个“超级木马病毒”的流程里有这么一个注册环节,要求用户输入帐号、密码、身份证号和姓名。

  身份证号是如何泄露的呢?如何泄露的呢?泄露的呢?的呢?病毒作者还是很用心的…输入姓名、身份证号、账号、密码点击注册后,悲剧了,信息发到木马制作者那了。

  至此,木马作者获得了修改支付宝密码、支付密码的所有需要的东西,于是…开工干活,转账…

  账号有修改密码时、转账操作后,支付宝均会通过“95188”这个号码发送短信到用户手机,不过估计用户也看不到短信了,木马全给拦截了..

  这里还有一点,就是账号和密码问题,如果你习惯性用相同的账号和密码去注册N个网站,那么泄露的就不止支付宝了,所以网上有专家也不止一次的提醒尽量不要使用相同的账号去注册各种账号,不然一个泄露,所有的网站信息全部可以被窃取了。

  基本上木马都是如上述的工作流程,来窃取用户的信息。

  为什么哭晕在厕所的总是支付宝?因为用的人多呀,支付宝一类的在线支付工具,如支付宝、财付通,基本上都可以通过上述操作来获取用户的信息。

  那么网银呢?网银为什么也中招?网银不是需要网银盾口令校验么?

  对呀,是需要呀,所以安全性相对来说也高了不少呀(至少在手机上是的..),但是别忘了支付宝绑定了银行卡,同样可以进行充值操作。

  另外网银也有手机支付,比如去12306买个火车票,选择建行支付,其中有一项就是通过发送个手机验证码完成支付宝的,当然,这还需要卡号、密码等等,如何泄露的,这个就得您自个想想了。

  上面的一堆分(fei)析(hua)到此结束了,下面笔者说说如何防范此类事情的发生。

   如何防范此类事情:

  1、首先最好给手机安装一个安全软件,比如360手机卫士、腾讯手机管家、金山毒霸、百度手机卫士、Avast!等,这些安全软件基本都能在下载和安装阶段拦截掉病毒。

  2、还可以安装一些如LBE安全大师一类的APP,APP在发送短信的时候弹出提醒,会弹出询问用户是否发送的提示;

  3、专家的话:不要随意扫描二维码、下载apk文件,更不要随意安装您不认识的APP;

  4、不要主动下载那些具有诱惑性的名字的APP,比如什么XX神器、看X神器、快X神器,这类一般多出现在某些你懂得小网站上面,多半不是病毒就是流氓软件;

   如果发现异常如何处理:

  1、安装了一个app,安装完却没有图标,怎么打开呢?不用找了,多半是中毒了,立马把手机开启飞行模式,并关闭Wlan,这样可以阻止病毒通过短信或联网方式发送用户信息到木马制作者那;当然,关机也行。

  2、开启飞行模式 、断开网络、关机以后先不要忙着怎么处理手机,去支付宝官方网站把余额转入关联的银行卡并解除绑定的银行卡;

  3、处理手机:断开网络(最好把手机卡拔了)没法下载安全软件怎么办,用数据线连接电脑去豌豆荚、360助手、腾讯助手之类的下载安装安全软件进行手机查杀;

  4、查杀了还不放心?备份下重要资料,如通讯录、短信、通话记录、照片等,豌豆荚、360助手、腾讯助手之类的同样有这些功能,备份后把手机恢复出厂设置、格式化内存卡,或者直接通过刷机精灵、刷机大师一类的软件对手机进行刷机操作。

  涉及到钱的问题,相信你不会嫌麻烦….

  还有这个“超级手机病毒”的传播问题,相信大家都知道了作者为某高校大一学生,已经被抓。这个病毒很复杂么?不复杂,甚至有些媒体发文说这类所谓的“超级”病毒,简直就是几个小工具拼凑出来的,如此简陋没想到还会这么大规模传播。

  笔者最早看到关于这个病毒的消息是在8月2日的下午13:15分,从知道创宇@余弦的微博上看到的,其中@余弦称这个病毒为半成品,在16:58分,@91ri_我的安全攻防发布了分析报告,包括传播方式、原理甚至最后连病毒作者的姓名、QQ、QQ密码、手机号码、住址和某高校学生的信息全部获得了。

  但是这些信息并没有引起足够的关注,在随后的一天及8月4日,媒体才大规模报道,同时运营商也发微博说开始拦截了,不过相信这会儿已经传播规模到非常大的级别了吧?

  嗯,所以,时常关注一下此类信息还是非常有用的,可以在第一时间获得这些信息,避免自己中招。

  来源:蓝点网原创文章投稿,作者:山外的鸭子哥, 原文链接

评论《Android手机真的那么容易中毒吗?》的内容...

相关文章:


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

Android UI 组件开源软件

$
0
0

其实也算不上合集,只是将我经常用到的部分整理一下,如果您有好东西,也可以留言补充,

可以参照应用Libraries for developers,ios的参照Libraries for developers Pro

1.actionbar

http://actionbarsherlock.com/

https://github.com/JakeWharton/ActionBarSherlock (推荐)

2.下拉刷新pulltorefresh

https://github.com/chrisbanes/Android-PullToRefresh

 

支持各种控件下拉刷新
ListView、ViewPager、WevView、ExpandableListView、GridView、(Horizontal

 


)ScrollView、Fragment上下左右拉动刷新,比下面johannilsson那个只支持ListView的强大的多。并且他实现的下拉刷新ListView在item不足一屏情况下也不会显示刷新提示,体验更好。

 

3.viewflow

https://github.com/pakerfeldt/android-viewflow

 

4.viewpagerIndicator

https://github.com/JakeWharton/Android-ViewPagerIndicator

直接继承自ViewPager,主要在onInterceptTouchEvent()和onTouchEvent()里面加入了对于垂直方向滑动的支持.并提供了setOrientation()这个方法

tab底部的下划线是会随着pager的滑动而实时变化,而论坛里很多demo则是标签页切换时下划线的动画效果,二者是有区别的

参照 http://blog.csdn.net/lancees/article/details/9164421

5.IndexableListView

https://github.com/woozzu/IndexableListView

 ListView右侧会显示item首字母快捷索引,点击可快速滑动到某个item

5.1CustomFastScrollViewDemo

https://github.com/nolanlawson/CustomFastScrollViewDemo

ListView快速滑动,同时屏幕中间PopupWindows显示滑动到的item内容或首字母

6.RefreshableListView

https://github.com/woozzu/RefreshableListView

 

7.NewQuickAction3D

https://github.com/lorensiuswlt/NewQuickAction3D

   

8.FlipAnimatorExample

http://code.google.com/p/myandroidwidgets

9.NineOldAndroids

https://github.com/JakeWharton/NineOldAndroids

10.AndroidWheel

https://github.com/sephiroth74/AndroidWheel

11.android-wheel

http://code.google.com/p/android-wheel

12.cardsui-for-android

https://github.com/nadavfima/cardsui-for-android

13.RibbonMenu

https://github.com/darvds/RibbonMenu 

14.SwitchButton

https://github.com/IssacWang/SwitchButton

15.undergarment

https://github.com/eddieringle/android-undergarment

16.仿flipboard动画

https://github.com/openaphid/android-flip

17.drag-sort-listview 支持拖拽顺序和左右滑动删除功能的自定义ListView

https://github.com/bauerca/drag-sort-listview

18.SideNavigation

https://github.com/johnkil/SideNavigation

19.PhotoView

https://github.com/chrisbanes/PhotoView

20.sidemenu

https://github.com/maddeye/android-sidemenu

21.仿flipboard动画

https://github.com/openaphid/android-flip

22.Holo风格

https://github.com/Prototik/HoloEverywhere

23.侧滑菜单slidingMenu

https://github.com/jfeinstein10/SlidingMenu

24.标题会固定在上方的listview

https://github.com/emilsjolander/StickyListHeaders

效果图 https://raw.github.com/emilsjolander/StickyListHeaders/master/demo.gif

25.仿flipboard动画

https://github.com/emilsjolander/android-FlipView

26.标题会固定在上方的gridview

https://github.com/TonicArtos/StickyGridHeaders

27.单个item会打开的listview

https://github.com/tjerkw/Android-SlideExpandableListView

28.转盘菜单

http://code.google.com/p/radial-menu-widget/

29.通讯录般的listview

https://github.com/woozzu/IndexableListView

30.范围seekbar

https://code.google.com/p/range-seek-bar/

31.范围seekbar2

https://github.com/edmodo/range-bar

32.msg (类似Toast)

https://github.com/johnkil/Android-AppMsg

33.AutoScaleTextView

https://bitbucket.org/ankri/autoscaletextview/src/fd5625ceb37b5cf18b8bf1e0c3c20dca569de80d?at=default

34.ProgressButton

https://github.com/f2prateek/progressbutton

35.PagerSlidingTabStrip

https://github.com/astuetz/PagerSlidingTabStrip

36.圆形progressbar

https://github.com/ymotoba/pinprogress

37.gesture-imageview (类似PhotoView可缩放,貌似比PView更强大)

https://github.com/jasonpolites/gesture-imageview

38.staggeredGridView 瀑布流

https://github.com/maurycyw/StaggeredGridView

39.Crouton (类似Toast和AppMsg)

https://github.com/keyboardsurfer/Crouton

可以让开发者对环境中的Toast进行替换的类,以一个应用程序窗口的方式显示,而其显示位置则由开发者自己决定。


40.android-menudrawer

https://github.com/SimonVT/android-menudrawer

 

41.ActionBar-PullToRefresh ActionBar的下拉刷新

https://github.com/chrisbanes/ActionBar-PullToRefresh


42. pinned-section-listview

https://github.com/beworker/pinned-section-listview

GroupName滑动到顶端时会固定不动直到另外一个GroupName到达顶端的ExpandListView


43.Left Locked Gallery 

 android官方的Gallery是center lock的 意思是你只能居中显示你选中的item

https://github.com/TheLevelUp/android-left-locked-gallery

44. Bootstrap3.0风格的组件

https://github.com/Bearded-Hen/Android-Bootstrap


45.android-swipelistview

https://github.com/47deg/android-swipelistview

46.PinnedHeaderListView

https://github.com/JimiSmith/PinnedHeaderListView

GroupName滑动到顶端时会固定不动直到另外一个GroupName到达顶端的ExpandListView

47. QuickReturnHeader

https://github.com/ManuelPeinado/QuickReturnHeader

ListView/ScrollView的header或footer,当向下滚动时消失,向上滚动时出现

48. Android-ScrollBarPanel

https://github.com/rno/Android-ScrollBarPanel

 ListView滑动时固定的Panel指示显示在scrollbar旁边

49.jazzylistview

https://github.com/twotoasters/JazzyListView

ListView及GridView item以特殊动画效果进入屏幕,效果包括grow、cards、curl、wave、flip、fly等等

50. ListViewAnimations

https://github.com/nhaarman/ListViewAnimations

 带Item显示动画的ListView,动画包括底部飞入、其他方向斜飞入、下层飞入、渐变消失、滑动删除等

51. DevsmartLib-Android 横向ListView

https://github.com/dinocore1/DevsmartLib-Android

/************************************************可能报错的解决方法(部分取自网络)*********************************************************/

23.SlidingMenu 

     <1>. 在github上有一个效果不错的开源库,SlidingMenu 最新的代码下载下来后,会报错:

      No resource found that matches the given name: attr 'homeAsUpIndicator'.

      No resource found that matches the given name 'Theme.Sherlock.Light.DarkActionBar'.


      原因是它本身又引用了另外一个开源库 ActionBarSherlock ,所以首先要做的就是去下载 ActionBarSherlock,引用其中的library。

      <2>. 这样,SlidingMenu 的示例代码就引用了两个library,这时候还不能用,项目报错:
      
      Jar mismatch! Fix your dependencies
     
       解决此问题的方法见 http://blog.csdn.net/java_crab/article/details/8477416 ,其实就是需要两个library使用的support包是一样的。

       <3>. 解决了上面的问题之后,还会出现下面的问题:
      可能报找不到getSupportActionBar等ActionBarSherLock的方法。原因是使用ActionBarSherLock的Activity需继承于SherlockActivity,修改SlidingMenu library中的SlidingFragmentActivity,让它继承于SherlockFragmentActivity,重新编译library导入。

        经过以上三个步骤之后,就可以成功的编译通过SlidingMenu提供的示例代码!

39.Crouton

需要引入actionbarshelock和viewpagerIndicator

其他貌似都引入easy



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


ITeye推荐



跨域解决方法

$
0
0

由于同源策略的限制,JavaScript跨域的问题,一直是一个比较棘手的问题,为了解决页面之间的跨域通信,大家煞费苦心,研究了各种跨域方案。之前也有小网同学分享过一篇“ 跨域,不再纠结” 开始照着尝试时还是有些不够明白的地方,深入了解之后,这里给大家补充一点更具体的做法。

先来看看哪些情况下才存在跨域的问题:

编号URL说明是否允许通信

1

http://www.a.com/a.js http://www.a.com/b.js

同一域名下

允许

2

http://www.a.com/lab/a.js http://www.a.com/script/b.js

同一域名下不同文件夹

允许

3

http://www.a.com:8000/a.js http://www.a.com/b.js

同一域名,不同端口

不允许

4

http://www.a.com/a.js https://www.a.com/b.js

同一域名,不同协议

不允许

5

http://www.a.com/a.js http://70.32.92.74/b.js

域名和域名对应ip

不允许

6

http://www.a.com/a.js http://script.a.com/b.js

主域相同,子域不同

不允许

7

http://www.a.com/a.js http://a.com/b.js

同一域名,不同二级域名(同上)

不允许(cookie这种情况下也不允许访问)

8

http://www.a.com/a.js http://www.b.com/b.js

不同域名

不允许

其中编号6、7两种情况同属于主域名相同的情况,可以设置domain来解决问题,今天就不讨论这种情况了。 对于其他跨域通信的问题,我想又可以分成两类, 其一(第一种情况)是a.com下面的a.js试图请求b.com下某个接口时产生的跨域问题。 其二(第二种情况)是当a.com与b.com下面的页面成父子页面关系时试图互相通信时产生的跨域问题,典型的应用场景如a.com/a.html使用iframe内嵌了b.com/b.html,大家都知道a.html内的js脚本试图访问b.html时是会被拒绝的,反之亦然。  第一种情况,目前主流的方案是JSONP,高版本浏览器支持html5的话,还可以使用XHR2支持跨域通信的新特性。  第二种情况,目前主要是通过代理页面或者使用postMessageAPI来做, 这也是今天要讨论的话题。 第二种情况,有这样一些类似的案例:a.com/a.html使用iframe内嵌了b.com/b.html,现在希望iframe的高度能自动适应 b.html的高度,使iframe不要出现滚动条。我们都知道跨域了,a.html是没办法直接读取到b.html的高度的,b.html也没办法把自 己的高度告诉a.html。 直接说可以用代理页面的方法搞定这个问题吧,但是怎么代理法,先来看下面这张图:

ky-1

图1

b.html与a.html是不能直接通信的。我们可以在b.html下面再iframe内嵌一个proxy.html页面,因为这个页面是放在 a.com下面的,与a.html同域,所以它其实是可以和a.html直接通信的,假如a.html里面有定义一个方法_callback,在 proxy.html可以直接top._callback()调用它。但是b.html本身和proxy.html也是不能直接通信的,所谓代理页面的桥 梁作用怎么实现呢? b.html内嵌proxy.html是通过一段类似下面这样的代码: <iframe id=”proxy” src=”a.com/proxy.html” name=”proxy” frameborder=”0″ width=”0″ height=”0″></iframe> 这个iframe的src属性b.html是有权限控制的。如果它把src设置成a.com/proxy.html?args=XXX,也就是给url加 一个查询字符串,proxy.html内的js是可以读取到的。对的,这个url的查询字符串就是b.html和proxy.html之间通信的桥梁,美 中不足的是每次通信都要重写一次url造成一次网络请求,这有时会对服务器及页面的运行效率产生很大的影响。同时由于参数是通过url来传递的,会有长度 和数据类型的限制,搜集的资料显示:

  • IE浏览器对URL的长度现限制为2048字节。
  • 360极速浏览器对URL的长度限制为2118字节。
  • Firefox(Browser)对URL的长度限制为65536字节。
  • Safari(Browser)对URL的长度限制为80000字节。
  • Opera(Browser)对URL的长度限制为190000字节。
  • Google(chrome)对URL的长度限制为8182字节。

上面的方法,通过迂回战术实现了b.html跟a.html通信,但是倒过来,a.html怎么跟b.html通信呢?嵌入在b.html里面的 proxy.html可以用top快速的联系上a.html,但是要想让a.html找到proxy.html就不容易了,夹在中间的 b.html生生把它们分开了,a.html没法让b.html去找到proxy.html然后返回给它。只能采用更迂回的战术了。 顺着前面b.html到a.html的通信过程,逆向的想一下,虽然a.html没有办法主动找到proxy.html,但是proxy.html可以反 过来告诉a.html它在哪里: 在proxy.html加这么一段脚本:

var topWin = top;
function getMessage(data) {
    alert("messageFormTopWin:" + data);
}
function sendMessage(data) {
    topWin.proxyWin = window;
    topWin.getMessage(data);
}

在a.html加这么一段脚本:

var proxyWin = null;
function getMessage(data) {
    alert("messageFormProxyWin:"+data);
    sendMessage("top has receive data:"+data);
}

function sendMessage(data) {
    if (null != proxyWin) {
        proxyWin.getMessage(data);
    }
}

也就是必须由proxy.html先主动发送一个消息给a.html,a.html得到proxy.html页面window的引用,就可以反过来 向它发送请求了。 现在a.html可以把消息发给proxy.html了,但是proxy.html怎么把消息转送到b.html?似乎这才是难点,因为它们之间才真正有 着“跨域”这一道鸿沟。 这回我们不再用前面那个iframe内嵌代理页面的方法再在proxy.html内嵌一个b.com下面的代理页面了,这样实在会给人感觉嵌的太深了,四 层。但是为了跨越这道鸿沟,b.com下面也加一个代理页面是免不的。不过现在我们要利用一下window.name。window.name有一个特 性,就是页面在同一个浏览器窗口(标签页)中跳转时,它一直存在而且值不会改变。比如我们在a.html中设置了window.name=”a”,然后 location.href=” http://b.com/b.html”跳转 后,b.html可以读取window.name的值为”a”;而且window.name的值长度一般可以到达2M,ie和firefox甚至可以达到 32M,这样的存储容量,足够利用起来做跨域的数据传递了。好吧,我们现在要做的就是当proxy.html拿到a.html发送过来的数据后把这个数据 写入window.name中,然后跳转到b.com下面的代理页面,我们这里假设是bproxy.html。bproxy.html读取到 window.name值后,通知给它父页面b.html就简单了。我们再来看这个过程可以用图大概示意一下:

ky-2

图2

图例中绿色的双向箭头表示可以通信,橙色的双向箭头表示不能直接通信。 最后我们简单看一下双向通信的实测效果:

ky-3

图3

b.html每次加载的时候都先给a.html发一个”连接请求”,让a.html可以找到proxy.html。所以页面第一次加载的时候会产生三个请求:

ky-4

图4

每次b.html向a.html发送消息的时候会产生一个请求:

ky-5

图5

每次a.html向b.html发送消息的时候会产生两个请求,其中一个是a.com/proxy.html向b.com/bproxy.html跳转产生的,另一个是b.html重新向a.html发起“连接请求”时产生的:

ky-6

图6

最后简单看一下实测的几个测试页面代码:  代码片段一,a.com/a.html:

<html xmlns="http://www.w3.org/1999/xhtml"><head><title>a.com</title></head><body><div id="Div1">
        A.com/a.html</div><input id="txt_msg" type="text" /><input id="Button1" type="button" value="向b.com/b.html发送一条消息" onclick="sendMessage(document.getElementById('txt_msg').value)" /><div id="div_msg"></div><iframe width="800" height="400" id="mainFrame" src="<a href="http://localhost:8091/b.com/b.htm">http://localhost:8091/b.com/b.htm</a>"></iframe><script type="text/javascript">
        var proxyWin = null;
        function showMsg(msg) {
            document.getElementById("div_msg").innerHTML = msg;
        }
        function getMessage(data) {
            showMsg("messageForm b.html to ProxyWin:" + data);
        }
        function sendMessage(data) {
            if (null != proxyWin) {
                proxyWin.getMessage(data);
            }
        }</script></body></html>

代码片段二,a.com/proxy.html:

<html xmlns="<a href="http://www.w3.org/1999/xhtml">http://www.w3.org/1999/xhtml</a>"><head><title>a.com</title></head><body><div id="Div1">A.com/proxy.html</div><div id="div_msg"></div><script type="text/javascript">
    var topWin = top;
    function showMsg(msg) {
        document.getElementById("div_msg").innerHTML = msg;
    }

    function getMessage(data) {
        showMsg("messageForm A.com/a.html:" + data + "<br/>两¢?秒?后¨®将?跳¬?转Áa到Ì?B.com/bproxy.html");
        window.name = data;
        setTimeout(function () { location.href = "<a href="http://localhost:8091/b.com/bproxy.htm">http://localhost:8091/b.com/bproxy.htm</a>" }, 2000);// 为了能让大家看到跳转的过程,所以加了个延时
    }

    function sendMessage(data) {
        topWin.proxyWin = window;
        topWin.getMessage(data);
    }

    var search = location.search.substring(1);
    showMsg("messageForm B.com/b.html:" + search);
    sendMessage(search);</script></body></html>

代码片段三,b.com/b.html:

<html xmlns="<a href="http://www.w3.org/1999/xhtml">http://www.w3.org/1999/xhtml</a>"><head><title>b.com</title></head><body><div id="Div1">
        B.com/b.html</div><input id="txt_msg" type="text" /><input id="Button1" type="button" value="向A.com/a.html发送一条消息" onclick="sendMessage(document.getElementById('txt_msg').value)" /><div id="div_msg"></div><iframe id="proxy" name="proxy" style="width: 600px; height: 300px"></iframe><script type="text/javascript">
        function showMsg(msg) {
            document.getElementById("div_msg").innerHTML = msg;
        }
        function sendMessage(data) {
            var proxy = document.getElementById("proxy");
            proxy.src="<a href="http://localhost:8090/a.com/proxy.htm?data">http://localhost:8090/a.com/proxy.htm?data</a>=" + data;
        }
        function connect() {
            sendMessage("connect");
        }
        function getMessage(data) {
            showMsg("messageForm a.html to ProxyWin:" + data);
            connect();
        }

        connect(); // 页面一加载,就执行一次连接
    </script></body></html>

码片段四,b.com/bproxy.html:

<html xmlns="<a href="http://www.w3.org/1999/xhtml">http://www.w3.org/1999/xhtml</a>"><head><title>b.com</title></head><body><div id="Div1">
        B.com/bproxy.html</div><div id="div_msg"></div><script type="text/javascript">
        var parentWin = parent;
        var data = null;

        function getMessage() {
            if (window.name) {
                data = window.name;
                parentWin.getMessage(data);
            }
            document.getElementById("div_msg").innerHTML = "messageForm a.com/proxy.html:" + data;

        }
        getMessage();         
    </script></body></html>

好吧,现在我必须把话锋调转一下了。前面讲的这么多,也只是抛出来一些之前我们可能会采用的跨域通信方法,事实上代理页面、url传参数和 window.name、甚至还有一些利用url的hash值的跨域传值方法,都能百度到不少相关资料。但它们都逃不开代理页面,也就不可避免的要产生网 络请求,而事实上这并不是我们的本意,我们原本希望它们能够直接在客户端通信,避免不必要的网络请求开销——这些开销,在访问量超大的站点可能会对服务器 产生相当大的压力。那么,有没有更完美一点的替代方案呢? 必须给大家推荐postMessage。postMessage 正是为了满足一些合理的、不同站点之间的内容能在浏览器端进行交互的需求而设计的。利用postMessage API实现跨域通信非常简单,我们直接看一下实例的代码:

代码片段五,A.com/a.html

<html xmlns="<a href="http://www.w3.org/1999/xhtml">http://www.w3.org/1999/xhtml</a>"><head runat="server"><title>A.com/a.html</title><script type="text/javascript">
        var trustedOrigin = "<a href="http://localhost:8091/">http://localhost:8091</a>";

        function messageHandler(e) {
            if (e.origin == trustedOrigin) {//接收消息的时候,判断消息是否来自可信的源,这个源是否可信则完全看自己的定义了。
                showMsg(e.data);//e.data才是真实要传递的数据
            } else {
                // ignore messages from other origins
            }
        }

        function sendString(s) {//发送消息
            document.getElementById("widget").contentWindow.postMessage(s, trustedOrigin);
        }

        function showMsg(message) {
            document.getElementById("status").innerHTML = message;
        }

        function sendStatus() {
            var statusText = document.getElementById("statusText").value;
            sendString(statusText);
        }

        function loadDemo() {
            addEvent(document.getElementById("sendButton"), "click", sendStatus);
            sendStatus();
        }

        function addEvent(obj, trigger, fun) {
            if (obj.addEventListener) obj.addEventListener(trigger, fun, false);
            else if (obj.attachEvent) obj.attachEvent('on' + trigger, fun);
            else obj['on' + trigger] = fun;
        }
        addEvent(window, "load", loadDemo);
        addEvent(window, "message", messageHandler);</script></head><body><h1>A.com/a.html</h1><p><b>源</b>: <a href="http://localhost:8090</p">http://localhost:8090</p</a>><input type="text" id="statusText" value="msg from a.com/a.html"><button id="sendButton">向b.com/b.html发送消息</button><p>接收到来自a.com/a.html的消息: <strong id="status"></strong>.<p><iframe id="widget" width="800" height="400" src="<a href="http://localhost:8091/PostMessage/Default.aspx%22%3E%3C/iframe">http://localhost:8091/PostMessage/Default.aspx"></iframe</a>></body></html>

代码片段六,B.com/b.html:

<html xmlns="<a href="http://www.w3.org/1999/xhtml">http://www.w3.org/1999/xhtml</a>"><head runat="server"><title>B.com/b.html</title><script type="text/javascript">
        //检查postMessage 是否可以用:window.postMessage===undefined
        //定义信任的消息源
        var trustedOrigin = "<a href="http://localhost:8090/">http://localhost:8090</a>";
        function messageHandler(e) {
            if (e.origin === "<a href="http://localhost:8090/">http://localhost:8090</a>") {
                showMsg(e.data);
            } else {
                // ignore messages from other origins
            }
        }

        function sendString(s) {
            window.top.postMessage(s, trustedOrigin); //第二个参数是消息传送的目的地
        }

        function loadDemo() {
            addEvent(document.getElementById("actionButton"), "click", function () {
                var messageText = document.getElementById("messageText").value;
                sendString(messageText);
            });
        }

        function showMsg(message) {
            document.getElementById("status").innerHTML = message;
        }

        function addEvent(obj, trigger, fun) {
            if (obj.addEventListener) obj.addEventListener(trigger, fun, false);
            else if (obj.attachEvent) obj.attachEvent('on' + trigger, fun);
            else obj['on' + trigger] = fun;
        }
        addEvent(window, "load", loadDemo);
        addEvent(window, "message", messageHandler);</script></head><body><h1>B.com/b.html</h1><p><b>源</b>: <a href="http://localhost:8091</p">http://localhost:8091</p</a>><p>接收到来自a.com/a.html的消息: <strong id="status"></strong>.<p><div><input type="text" id="messageText" value="msg from b.com/b.html"><button id="actionButton"> 向a.com/a.html发送一个消息</button></div></body></html>

代码的关键是message事件是一个拥有data(数据)和origin(来源)属性的DOM事件。data属性是发送的实际数据,origin 属性是发送来源。Origin属性很关键,有了这个属性,接收方可以轻易的忽略掉来自不可信源的消息,也就能有效避免跨域通信这个开口给我们的源安全带来 的隐患。接口很强大,所以代码很简单。我们可以抓包看一下,这个通信过程完全是在浏览器端的,没有产生任何的网络请求。同时这个接口目前已经得到了绝大多 数浏览器的支持,包括IE8及以上版本,参见下面的图表:

ky-7

图7

但是为了覆盖ie6等低版本浏览器,我们完整的方案里面还是要包含一下兼容代码,就是最开始介绍的代理页面的方法了,但必须是以postMessage为主,这样即便最后会有某些浏览器因为这种通信产生一些网络请求,比例也是非常低的了。

转自:http://tid.tenpay.com/?p=4695

Viewing all 15843 articles
Browse latest View live


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