Monthly Archive: 十月 2015

重要的五分钟(Give it five minutes)

Something Go First

这是一篇译文,来自Jason Fried的《Give it five minutes》,读完对自己触动很大,曾经或者当下的自己都像极了年轻时候的Jason Fried,当然没有他如此大的成就,这篇文章中的观点值得自己借鉴:「先学会聆听,思考,再据理力争」,做一个会思考深刻的人。

[player autoplay=”1″]

译文正文

几年前,我还是一个行事莽撞的人。无论别人说了什么,我都认为他说的不对。如果某件事不符合我的世界观,我则会狠狠的反击。

这就比如说我才是第一个应该提出某种观点的人,似乎成为意见领袖对我来说是一件非常重要的事情。然而事实是我对问题缺乏深度的思考,你反驳的越快,说明你思考的越少,虽然并不总是这样,但几乎是

谈起膝跳反射时很轻松随意,好像这只是别人才会有的反应,你也有。如果你的邻居没有免疫能力,那么你也没有。

时间退回到2007年,当时我正在Providence, RI举办的商业创新工厂会议(Business Innovation Factory conference)上发言,Richard Saul Wurman也安排了发言。在我的演讲过后,他上来向我介绍他自己,同时赞扬了我的发言,他是如此大方,显然他不需要这么做。

但是你知道我接下来都做了些什么吗!?我对他的方言进行了狠狠的反驳。当时他在台上发言表述他的观点,而我在创新方面上和他意见相佐。等到会议间隙我有机会跟他对话时,我对他的某些观点进行了急不可耐的反驳。当时我的样子看起来像极了傻瓜。

然而他的回应改变了我的一生。『这简单』他说,『伙计,先思考五分钟』,我问这是什么意思?他说:『不同意我的观点没关系,对我的观点进行反驳也无所谓,并且坚守自己的观点和信念是一件好事,但是在你确认反驳我的观点之前能否给我的观点留点时间,先思考一下』。原来他说的「五分钟」代表「思考」,而不是当即反驳。关于这一点,他完全正确。我急匆匆的介入这次讨论只是想证明我的观点才是正确的,而这并不能让我学到什么。

这是我人生当中一个重要时刻。

Richard花了整整30年的职业生涯时间来思考这些问题,形成他自己的观点,然而我在几分钟内就立刻进行反驳。放到现在来看,当然他可能是错的,我是对的。但是在你对某件事非常确定你是对的之前,不妨先深入思考下。

另外问问题和反驳观点之间也有很大的不同,反驳意味着你对自己了解的问题进行了思考,已经形成自己的观点;问问题是说你想要去了解。这个世界如此之大,多多问问题。

先思考而不是立即反驳是我们一生的追求,这很难。我仍然有时会头脑发热,但是我真的很享受情况变得越来越好时所带给我的好处。

如果你不确定这一点为什么如此重要,你可以思考一下下面这段Jonathan Ive看待Steve Jobs对Idea是如此珍惜的一段话:

And just as Steve loved ideas, and loved making stuff, he treated the process of creativity with a rare and a wonderful reverence. You see, I think he better than anyone understood that while ideas ultimately can be so powerful, they begin as fragile, barely formed thoughts, so easily missed, so easily compromised, so easily just squished.

阐述的如此深刻,Idea是如此脆弱,它们开始时显得如此无力,几乎看不见,很容易就被我们忽视了。

在这个世界上有两件事情看起来是不需要掌握任何技能的:

  1. 花别人的钱;
  2. 对Idea置若罔闻;

错过一个Idea显得如此容易,因为它不牵涉任何工作。难的是你要小心保护它,对它用尽心思,让它生根发芽。

所以下次你听到某人说了什么,谈论了什么,或者提出了某个想法,先深入思考五分钟吧,在你狠狠反驳之前,你可能会认为他们说的这些事情太难了,或者有太多的工作需要完成,也许你认为的都对,但也有可能相反:恰好值得一试!

React’s diff algorithm(中文版)

Something go First

这是一篇介绍diff算法的翻译文章,原文来自FB工程师 Christopher Chedeau所写的《React’s diff algorithm》,特此说明,Ok,Here we go.

Background

React是由Facebook所发起的一个用于构建UI的Js库,创始初衷在于提高Js性能。在这篇文章中,我将使用图文的方式来呈现Diff算法和渲染是如何在React中工作的,并基于此原理,读者可以根据实际情况优化你的应用。

Diff Algorithm

首先,在正式开始了解React的实现细节之前,让我们从大体上来看下它是如何工作的:

在任何时候,你都能通过代码来描述你想要的UI效果。但是请记住,如果你使用了React库来描述你的UI,那么你并不是真正得到了真实页面上的DOM,得到的只是保存在内存中的一些轻量的Js对象,我们把这些对象称之为虚拟DOM。

React之所以能够大大提高网站性能,是因为React仅仅执行前一页面渲染成后一页面时所需要执行的最少差异动作,省去很多DOM属性值中间变换的渲染过程。

逐层比较(Level By Level)

在一般情况下,计算任意两棵二叉树之间的最少差异是一个O(n^3)的问题,你可以想象,在React实现中采用这种算法是不可取的,因此React设计了一种时间性能接近O(n)的启发式算法。
算法原理是React对任意两棵二叉树进行逐层比较,以此来确定这两棵二叉树之间的最少差异数。利用该方法大大降低了比较复杂度,按照通常来理解,这种比较方法是缺乏准确性的,但是在实际应用中观察发现:在DOM树中移动上下级节点的情况是非常少见的,通常只是在同一层的兄弟节点之间移动,如下图所示:

level

列表(List)

假设我们现在有一个组件,在一次迭代循环中,渲染出五个组件,接下去在该组件列表中插入一个新的组件,在信息如此匮乏的情况下,实现两个组件列表之间的映射是一件非常困难的事情。
在默认情况下,React会把前一个列表中的第一个组件与后一个列表中的第一个组件进行配对,以此类推,当然,你可以为每个组件提供一个Key属性来解决映射问题。在实际应用中,我们能够很容易的在孩子节点中找出唯一的Key,如下图所示:

list

组件(Components)

一个React应用通常是由许多用户自定义的组件组成,这些组件最终转化成一棵主要由div(s)组成的DOM树。这一辅助信息被diff算法所很好的利用,因为React通常只比对那些具有相同类的组件
举例,假设现在Header被一个ExampleBlock所代替,React将会简单粗暴的把Header标签移除,然后创建ExampleBlock,而不会浪费时间去比对这两个毫无相似之处的组件,如下图所示:

 

companet

事件代理(Event Delegation)

为DOM节点设置事件监听器是一件非常耗时和消耗内存的痛苦事。不过,React实现了一项更为流行的技术,叫做”事件代理”,更进一步的是,React还重新实现了一套遵循W3C标准的事件系统,这意味着IE8中原有的事件处理的Bug已成往事,事件名称在各个浏览器之间得以保持一致。
让我来解释一下这是如何实现的:单事件监听器与文档根节点进行绑定,当触发某一事件时,浏览器给了我们想要的DOM节点。因此,React为了在DOM层级间传播事件,并没有在虚拟DOM层级间迭代循环。
取而代之的是我们巧妙地利用了每个React组件都有一个编码层级的唯一标示符ID这样一个事实。通过简单的字符串操作我们能很容易的获取到所有父节点的ID。同时把事件监听器放在一个Hashmap中,我们发现这样做的性能远高于把事件监听器与虚拟DOM节点进行绑定。下面的代码展示了事件是如何在虚拟DOM间进行分发的:

浏览器为每个事件和每个监听器都创建一个新的事件对象,这样看起来很好,因为我们能追踪到每个事件对象甚至修改它,然而这也意味着需要许多次的内存分配操作。而React会在应用启动时在内存中分配一个对象池,无论何时需要事件对象,都可以从这个对象池中获取一个可用的事件对象进行复用,这一举措大大降低了垃圾回收的复杂度。

渲染(Rendering)

批处理(Batching)

无论何时在组件上触发setState方法,React都会把这个组件标记为dirty,等到结束事件循环时,React会对所有标记为dirty的组件进行批处理,重新渲染它们。
该批处理意味着在一个事件循环期间,无论某个DOM元素的属性值被更新过几次,最终在循环结束之后,该属性值只会被渲染一次,这对于构建一款高性能的应用至关重要,而在原生的JS中要实现这个功能却是一项非常困难的工作,而在React应用中,该功能与生俱来,如下图所示:

batching
子树渲染(Sub-tree Rendering)

当触发某个组件的setState时,该组件将会为其孩子节点重新构建虚拟DOM。如果你不幸调用了根元素的setState方法,那么整个React应用都将重新渲染,所有组件的render方法都将会被调用,即使这些节点根本就没有发生改变,这听起来很吓人,然而在实际应用中,并不如此,因为我们仅仅只操作了保存在内存中的虚拟DOM,而并没有碰真实的DOM。
首先,我们正在讨论的是展示用户界面。因为屏幕大小是有限制的,而你通常需要一次性的有顺序的展示上百甚至上千个元素。
另一个关键点是当你在写React代码时,你并不会常常调用根节点的setState方法。你只对那些受到事件触发的组件进行setState的调用,这意味setState方法的调用常常局限在与用户交互的组件上,很少会调用根节点的setState方法。

sub-tree

选择性的子树渲染(Selective Sub-tree Rendering)

最后,你也可以通过在组件上实现下面的方法来防止其子树进行渲染。

基于组件的前后状态,你可以告诉React该组件并没有发生变化,所以无需重新渲染。如果你能够正确使用这项功能,那么这对你应用的性能是一个极大的提升。

为了能够使用该功能,你必须能够比较JS对象,但是这会带来很多问题,比如我们应该进行何种程度的比较,深还是浅,如果进行深层次的比较,那我们也许应该使用不可变的数据结构或者进行深度拷贝。
并且你还要去考量,如果使用此项了功能,那么我们要确保比较JS对象所带来的计算时间要少于未使用此项功能而需要进行渲染的时间。

结论(Conclusion)

未翻译,全文完。转载请联系作者

triffic.tang@gmail.com

Reference

  1. http://calendar.perfplanet.com/2013/diff/
  2. http://blog.vjeux.com/

 

Piwik实践入门介绍

Background

最近团队内部想利用piwik作为网站流量分析工具,刚好借此机会接触之,用过之后才发现,piwik强大又精细,强大之处表现在其功能性完全可以媲美主流的商用流量分析工具,如Google Analystics,百度流量统计等;精细之处表现在给用户很强的惊喜感,体验到很多意想不到的细节,用户接触之后自有体会。可以在线体验:ONLINE DEMO,写这篇文章的目的在于总结这几天的学习心得,算是一个回顾,也把在学习研究过程中遇到的困惑进行记录。

Install

安装分『安装需要满足的条件』和『安装过程』,同时官网还提供了安装视频,这一切尽在:Installation

New to piwik

官网上该板块的主要目的是带新用户进入piwik的世界,了解piwik的工作机制甚至会涉及到与同类型的流量统计工具的横向比较,是新用户了解piwik的最好实践:New to Piwik

How to

该板块主要介绍如何玩转piwik,包括如何定制个性化变量,如何提高piwik运行的性能等等一系列让你更深入玩转piwik的相关建议和最佳实践:How to

Troubleshooting

不管你是新用户还是历史悠久的老用户,这里一定是会来逛逛的,可以说该板块是问题集,遇到问题是不可避免的,不妨来这里看看,或许前人已经给出了很好的解答:Troubleshooting

一些思考的问题(Q&A)

1、访问次数,访客数(独立),浏览次数如何确定?
1)浏览次数基于追踪的js代码有没有被加载,加载则+1,建议把追踪代码放在公共页面;
2)访问次数表示访问被监测网站的用户数(用户不去重);基于first party uuid和ip,session过期之后,重新生成uuid,访问次数+1;
3)访客数指独立访客数,在访问次数的基础上去掉重复的用户;基于ip地址的启发式算法;

2、PIWIK页面打开速度很慢?
官网提到的可能原因是:在默认情况下,piwik实时处理用户发出的请求(页面点击操作)生成报表进行展示,所以后台计算以及页面渲染展示的时间会比较长。
解决方案是:在crontab中设置定时任务,定时进行报表的归档。理论上可以提高访问速度,设置定时任务

3、访客数如果想汇总体现,如何在页面提现?
开启相关标志位即可:

在默认情况下,出于性能的考虑,piwik关闭了按年和按指定的时间段进行访客数统计的功能,因为按年和按指定时间段进行访客数统计时,需要实时去扫描piwik的log,所以耗时很长,对高流量的网站来说,可能需要长达几分钟的处理时间。

Reference

  1. http://piwik.org/

BI模式(SCHEMA)介绍-访问权限配置

目录

  1. Table配置
  2. Dimension配置
  3. Measure配置
  4. VirtualCube配置
  5. 访问权限配置

5、访问权限配置

1、该部分属于访问控制配置文件
2、Role定义了一个访问者
3、SchemaGrant是一个访问Schema的权利的集合
4、CubeGrant是一个访问Cube的权利的集合
5、HierarchyGrant是一个访问该层次以及属于该层次的级别的权利的集合
6、MemberGrant是一个访问成员以及该成员的孩子的权利的集合
在理解上述元素的情况下,我们来了解在本sample中该部分的作用:首先定义了一个访问用户,是California manager(加州经理),他不能访问整个Schema中的Cube,但他可以访问Sales这个Cube。其次,自定义了对该Sales Cube中的Store这个层次的访问控制,他可以访问位于美国加州的store(除了洛杉矶)。最后定义了他对该Sales Cube中的Gender层次无访问权限。
7、第五部分的完整框架是:

BI模式(SCHEMA)介绍-VirtualCube配置

目录

  1. Table配置
  2. Dimension配置
  3. Measure配置
  4. VirtualCube配置
  5. 访问权限配置

4、VirtualCube配置

1、VirtualCube,A cube defined by combining the dimensions and measures of one or more cubes. A measure originating from another cube can be a <CalculatedMember>.即若干个Cube中的dimension和measure相结合形成一个Cube,该Cube就叫做VirtualCube。来自另一个Cube的measure可以是该VirtualCube的计算成员。

2、在本sample中定义了一个叫Warehouse and Sales的多维虚拟分析主题,VirtualCubeDimension定义了一个来自Sales的Cube,并且选择了该Cube中的Customers维。注意在VirtualCubeDimension的定义中还有另一种方法,如<VirtualCubeDimension name=”Product”/>,这是针对共享维的使用方法。
3、VirtualCubeMeasure定义了一个来自Sales的Cube,并且选择了该Cube中的Sales Count度量。
4、CalculatedMember定义了一个计算成员,使用方法和一般的Cube一样。
5、第四部分一个完整的框架是:

或者如本sample中的写法,即把在VirtualCube中要使用到的Cube写在<VirtualCubeDimension>,<VirtualCubeMeasure>和<CalculatedMember>中,从而不需要在<CubeUsages>中定义。

BI模式(SCHEMA)介绍-Measure配置

目录

  1. Table配置
  2. Dimension配置
  3. Measure配置
  4. VirtualCube配置
  5. 访问权限配置

3、Measure配置

1、第一个Measure的名字是Unit Sales,对应事实表中的unit_sales列,聚合的方法是求和(还可以是:sum,count,min,max,avg,distinct-count),求和之后的结果格式为标准格式。

2、第二个Measure的名字是Promotion Sales,在该Measure下加入了一个MeasureExpression。期望是:从事实表sales_fact_1997中挑选出promotion_id=0的所有行,然后对该事实表的store_sales进行求和(如果某行的sales_fact_1997.store_sales=0,则不对该行进行计算)
3、CalculatedMember元素是一个计算成员,在本sample中取名Profit,属于Measures维。期望是:商店的销售额减去商店的成本得到利润值,得到的值的类型是字符串类型,值的格式是$#,##0.00
4、第三部分一个完整的框架是:

BI模式(Schema)介绍-Dimension配置

目录

  1. Table配置
  2. Dimension配置
  3. Measure配置
  4. VirtualCube配置
  5. 访问权限配置

2、Dimension配置

1、DimensionUsage元素:如果在之前定义了共享维(shared dimensions),并且在该Cube中想要使用该共享维,那么就如<DimensionUsage name=”Store” source=”Store” foreignKey=”store_id”/>使用,通过store_id把事实表sales_fact_1997和基础表Store相连接。
2、
定义了一个名为Promotion Media的维,通过promotion_id连接基础表promotion和事实表sales_fact_1997。<Level name=”Media Type” column=”media_type” uniqueMembers=”true”/>定义了一个层次中的级别,选择了基础表promotion的media_type字段,不包括该表中的其他字段。
3、在本上面的例子中又定义了一个名为Customers的demension,通过字段customer_id连接基础表customer和事实表sales_fact_1997。接下来来解释下其他元素的作用:
4、第二部分的完整框架是:

BI模式(Schema)介绍-Table配置

在这里系统的看看构成模式文件的元素:
首先说明一点:这个sample中每一部分在形式上有很多重复的地方,为了尽可能地讲的完全,所以在讲解的过程中,每一部分提取形式上不重复的部分,然后加入该部分的一些可选项,同时可以查阅FoodMart.xml这个Schema文件。从宏观上来看,配置一个Schema大致可以分成五部分,分别是:

  1. Table配置
  2. Dimension配置
  3. Measure配置
  4. VirtualCube配置
  5. 访问权限配置

下面分析每一部分中的元素构成。

1、Table配置

  1. 首先给出表名,在一般情况下,一个Cube是建立在一张事实表之上的,所以Table name=某事实表,但也可以在一张基础表上建立Cube;
  2. 在本sample中加入了一个聚合表(aggregate table),在一个Cube中可以加,也可以不加,需要根据实际情况来决定。聚合表的作用简单的说表现在考虑报表设计时,通过聚合预先计算好数据汇总,从而改进查询响应的时间。聚合表的内容非常丰富,在这里只讲解最基本的概念,进一步了解请访问官网http://mondrian.pentaho.com/documentation/aggregate_tables.php,接下来简单的理解构成聚合表的元素;
  3. AggExclude name,这里给出的表名旨在告诉Mondrian该表不是相应事实表的聚合表;
  4. AggName name,这里给出的表名告诉Mondrian该表是相应事实表的聚合表;
  5. AggFactCount column=”FACT_COUNT”,这个字段在每一张聚合表中都存在,记录了相应的事实表中有多少列写入到聚合表中;
  6. AggIgnoreColumn column,该列名的作用是告知Mondrian该列是已知的,应该被忽略;
  7. <AggForeignKey factColumn=”product_id” aggColumn=”PRODUCT_ID”/>这里给出了一个映射关系,即事实表中的product_id列对应了聚合表中的PRODUCT_ID列;
  8. AggMeasure name和AggLevel name这两个元素同样也给出了一个映射关系,即把在Cube的模式文件中定义的逻辑名映射到聚合表中的列名;
  9. AggPattern pattern使用了正则表达式,把符合该pattern的聚合表包括进来。如在本sample中把前缀为agg_sales_fact_1997_的聚合表全部包括进来作为孩子元素;
  10. AggPattern pattern中的AggExclude name元素,该元素很明确地指出在已包括进来的聚合表中,剔除某特定的聚合表;
  11. AggPattern pattern中的AggExclude pattern元素,该元素的作用表现在把符合AggExclude pattern的正则表达式的聚合表剔除;
  12. 第一部分的完整框架是:
 

Logback日志输出规则

Background

LOGBACK作为一款优秀的开源日志组件,相信每位都多少接触过,这里记录几个比较容易忽略却重要的Points(主要指日志输出规则),下面是Logback中关于根节点和子节点之间的继承关系图。

logback1
关于Appender部分

这部分内容就不再说明,关于如何布局日志的输出,网上相关内容已经很多,可以查阅参考文献中给出的链接。
Logger和Root在输出日志时的规则:
Logger用来指定项目源码中某一个Package或者某一个class所对应的日志打印级别,所选用的appender,同时还有一个可选项为addtivity,共三个属性,具体解释如下:

Name:指定项目源码包或者类的路径,为该包或类定义独特的日志格式,一般为com.xxx;
Level:指定输出日志的最低级别,一般为下列中的某一项(缺省情况下使用上级的Level):

Trace
Debug
Info
Warn
Error
ALL
OFF
INHERITED or NULL 表示强制使用上级的Level配置

Addtivity:是否向上级Logger传递日志输出信息,缺省情况下为True。

Root从本质上说其也是一个Logger元素,只不过Root只有一个Level属性
日志输出规则如下图所示:

logback2

Reference

  1. http://my.oschina.net/looly/blog/298675

BOOKLIST OF 2015

Background

2015年已过一大半,年初制定的每读完一本书就写一篇Blog的愿望最终还是没能实现,原因有:

  1. 书读的多了,发现很多思想放在心底会更好,写出来感觉都变味了,原谅我功力不够,怕写出几段骚柔文字侵犯了经典;
  2. 对每一本读完的书写类似读后感的文字,其实是对书中所传导的观点的一种思考,这种思考在我看来是与时俱进的,并不能由当时当下就能下定论,随着时间的推移,细细想之,总能体会出别样的味道,所以还是别定型了;
  3. 我害怕把写读后感变成一种精神上的任务,导致一种让我带着偏离正常轨道的思考去阅读一本书,意思是在阅读过程中就一直在思考如何写这篇书评,却忽略了阅读本身。

所以决定列一份书单,带上简单的几句点评,让我的阅读变得更轻松:

Sept

1、[A Farewell To Arms], 《永别了,武器》 By 海明威(Ernest Hemingway)

经历战争洗礼的男人才懂得战争的真实残酷,我们祈祷有一个美满的结局,但留下的依旧是被战争迫害后的支离破碎,了解战争的残酷,洞悉人性的险恶,惟一让我们能得到慰藉的是人与人之间的爱。

2、[Sams Teach Yourself Regular Expressions in 10 Minutes], 《正则表达式必知必会》 By Ben Forta

由Ben Forta所著的这本正则表达式书籍入门简单,实用性非常强,让人在掌握基本的正则表达式语法之后,会在实际开发过程中有意识的开始思考如何利用正则表达式帮我们做代码优化(很多用代码实现的字符串搜索和替换又臭又长,而这正是正则表达式发挥其神力的地方),删除冗余代码,写出具备实际意义又优雅的代码,赞!

3、[ナミヤ雑貨店の奇蹟], 《解忧杂货店》 By (日)东野圭吾

浪矢杂货店连接着过去与未来,三位年轻人天注定似的逃生来到此,由此展开了一段匪夷所思的精神冒险,从解决他人的烦恼开始,意识到自己对于他人的重要性,最后作者神迹般的让这三位年轻人完成了从不良少年到自我肯定的救赎。作者构思巧妙,中心意图明显,只是读者真的能很好理解作者在构思浪矢杂货店作为链接过去和未来这一桥梁角色的逻辑合理性吗?

4、《嫌疑人X的献身》 By (日)东野圭吾

故事精彩之处在于敌我双方的一攻一守,也即石神达摩和汤川学之间的较量,如果没有汤川学这个角色,很难想象警察能解开这个谜。从这个故事中读出了石神内心的足智多谋,勇敢和无所畏惧,真正为爱献身。

5、白夜行 By  (日)东野圭吾

读完这本小说,内心深处是感到震撼的,不得不说,作者讲述故事的逻辑很强,前后贯通,同时最精彩的是这个故事本身。

 

 

Reference

http://readcolor.com/books/52da22803063e1d3b1000002