开发人员必知的5种开源框架

作者:John Esposito

软件侵吞着世界已经四年多了,但开发人员看待软件的方式稍有不同。我们一直在致力于解决实际问题,而很少思考软件开发的基石。当问题变得更庞大、解决方案更复杂时,一些实用的、不怎么产生泄漏的抽象工具就显得越来越重要。

简单地来说,在那些追求生产效率的开发者眼中,框架正在吞食着世界。那究竟是哪些框架、各自又在吞食着哪一部分呢?

开源界的开发框架实在太多了,多到近乎疯狂的地步。我从2015年各种领域的榜单中选取了最受欢迎的5种框架。对于前端框架(我所擅长的领域),我只选取那些真正的客户端框架,这是因为现今的浏览器和移动设备已经具备非常好的性能,越来越多的单页应用(SPA)正在避免和服务端交换数据。

1. 展现层:Bootstrap

我们从技术栈的顶端开始看——展现层,这一开发者和普通用户都会接触到的技术。展现层的赢家毫无疑问仍是Bootstrap。Bootstrap的流行度非常之惊人,远远甩开了它的老对手Foundation,以及新星Material Design Lite。在BuiltWith上,Bootstrap占据主导地位;而在GitHub上则长期保持Star数Fork数最多的记录。

如今,Bootstrap仍然有着非常活跃的开发社区。8月,Bootstrap发布了v4内测版,庆祝它的四岁生日。这个版本是对现有功能的简化和扩充,主要包括:增强可编程性;从Less迁移至Sass;将所有HTML重置代码集中到一个模块;大量自定义样式可直接通过Sass变量指定;所有JavaScript插件都改用ES6重写等。开发团队还开设了官方主题市场,进一步扩充现有的主题生态

2. 网页MVC:AngularJS

随着网页平台技术越来越成熟,开发者们可以远离仍在使用标记语言进行着色的DOM对象,转而面对日渐完善的抽象层进行开发。这一趋势始于现代单页应用(SPA)对XMLHttpRequest的高度依赖,而其中流行的SPA框架当属AngularJS

AngularJS有什么特别之处呢?一个词:指令(directive)。一个简单的ng-就能让标签“起死回生”(从静态的标记到动态的JS代码)。依赖注入也是很重要的功能,许多Angular特性都致力于简化维护成本,并进一步从DOM中抽象出来。其基本原则就是将声明式的展现层代码和命令式的领域逻辑充分隔离开来,这种做法对于使用过POM或ORM的人尤为熟悉(我们之中还有人体验过XAML)。这一思想令人振奋,解放了开发者,甚至让人第一眼看上去有些奇怪——因为它赋予了HTML所不该拥有的能力。

有些遗憾的是,AngualrJS的“杀手锏”双向绑定(让视图和模型数据保持一致)将在Angular2中移除,已经临近公测。虽然这一魔法般的特性即将消失,却带来了极大的性能提升,并降低了调试的难度(可以想象一下在悬崖边行走的感觉)。随着单页应用越来越庞大和复杂,这种权衡会变得更有价值。

Read More

使用Spring AOP向领域模型注入依赖

贫血领域模型这篇译文中,Martin阐述了这种“反模式”的症状和问题,并引用了领域驱动设计中的话来说明领域模型和分层设计之间的关系。对于Spring项目的开发人员来说,贫血领域模型十分常见:模型(或实体)仅仅包含对数据表的映射,通常是一组私有属性和公有getter/setter,所有的业务逻辑都写在服务层中,领域模型仅仅用来传递数据。为了编写真正的领域模型,我们需要将业务逻辑移至模型对象中,这就引出另一个问题:业务逻辑通常需要调用其他服务或模型,而使用new关键字或由JPA创建的对象是不受Spring托管的,也就无法进行依赖注入。解决这个问题的方法有很多,比较之后我选择使用面向切面编程来实现。

面向切面编程

面向切面编程,或AOP,是一种编程范式,和面向对象编程(OOP)互为补充。简单来说,AOP可以在不修改既有代码的情况下改变代码的行为。开发者通过定义一组规则,在特定的类方法前后增加逻辑,如记录日志、性能监控、事务管理等。这些逻辑称为切面(Aspect),规则称为切点(Pointcut),在调用前还是调用后执行称为通知(Before advice, After advice)。最后,我们可以选择在编译期将这些逻辑写入类文件,或是在运行时动态加载这些逻辑,这是两种不同的织入方式(Compile-time weaving, Load-time weaving)。

对于领域模型的依赖注入,我们要做的就是使用AOP在对象创建后调用Spring框架来注入依赖。幸运的是,Spring AOP已经提供了@Configurable注解来帮助我们实现这一需求。

Read More

贫血领域模型

原文:http://www.martinfowler.com/bliki/AnemicDomainModel.html

贫血领域模型是一个存在已久的反模式,目前仍有许多拥趸者。一次我和Eric Evans聊天谈到它时,都觉得这个模型似乎越来越流行了。作为领域模型的推广者,我们觉得这不是一件好事。

贫血领域模型的最初症状是:它第一眼看起来还真像这么回事儿。项目中有许多对象,它们的命名都是根据领域来的。对象之间有着丰富的连接方式,和真正的领域模型非常相似。但当你检视这些对象的行为时,会发现它们基本上没有任何行为,仅仅是一堆getter和setter的集合。其实这些对象在设计之初就被定义为只能包含数据,不能加入领域逻辑。这些逻辑要全部写入一组叫Service的对象中。这些Service构建在领域模型之上,使用这些模型来传递数据。

这种反模式的恐怖之处在于,它完全是和面向对象设计背道而驰。面向对象设计主张将数据和行为绑定在一起,而贫血领域模型则更像是一种面向过程设计,我和Eric在Smalltalk时就极力反对这种做法。更糟糕的时,很多人认为这些贫血领域对象是真正的对象,从而彻底误解了面向对象设计的涵义。

Read More

View Spark Source in Eclipse

Reading source code is a great way to learn opensource projects. I used to read Java projects’ source code on GrepCode for it is online and has very nice cross reference features. As for Scala projects such as Apache Spark, though its source code can be found on GitHub, it’s quite necessary to setup an IDE to view the code more efficiently. Here’s a howto of viewing Spark source code in Eclipse.

Install Eclipse and Scala IDE Plugin

One can download Eclipse from here. I recommend the “Eclipse IDE for Java EE Developers”, which contains a lot of daily-used features.

Then go to Scala IDE’s official site and install the plugin through update site or zip archive.

Generate Project File with Maven

Spark is mainly built with Maven, so make sure you have Maven installed on your box, and download the latest Spark source code from here, unarchive it, and execute the following command:

1
$ mvn -am -pl core dependency:resolve eclipse:eclipse

Read More

HotSpot JVM中的对象指针压缩

原文:https://wiki.openjdk.java.net/display/HotSpot/CompressedOops

什么是一般对象指针?

一般对象指针(oop, ordinary object pointer)是HotSpot虚拟机的一个术语,表示受托管的对象指针。它的大小通常和本地指针是一样的。Java应用程序和GC子系统会非常小心地跟踪这些受托管的指针,以便在销毁对象时回收内存空间,或是在对空间进行整理时移动(复制)对象。

在一些从Smalltalk和Self演变而来的虚拟机实现中都有一般对象指针这个术语,包括:

部分系统中会使用小整型(smi, small integers)这个名称,表示一个指向30位整型的虚拟指针。这个术语在Smalltalk的V8实现中也可以看到。

为什么需要压缩?

LP64系统中,指针需要使用64位来表示;ILP32系统中则只需要32位。在ILP32系统中,堆内存的大小只能支持到4Gb,这对很多应用程序来说是不够的。在LP64系统中,所有应用程序运行时占用的空间都会比ILP32大1.5倍左右,这是因为指针占用的空间增加了。虽然内存是比较廉价的,但网络带宽和缓存容量是紧张的。所以,为了解决4Gb的限制而增加堆内存的占用空间,就有些得不偿失了。

在x86芯片中,ILP32模式可用的寄存器数量是LP64模式的一半。SPARC没有此限制;RISC芯片本来就提供了很多寄存器,LP64模式下会提供更多。

压缩后的一般对象指针在使用时需要将32位整型按因数8进行扩展,并加到一个64位的基础地址上,从而找到所指向的对象。这种方法可以表示四十亿个对象,相当于32Gb的堆内存。同时,使用此法压缩数据结构也能达到和ILP32系统相近的效果。

我们使用解码来表示从32位对象指针转换成64位地址的过程,其反过程则称为编码

Read More

Spark Streaming Logging Configuration

Spark Streaming applications tend to run forever, so their log files should be properly handled, to avoid exploding server hard drives. This article will give some practical advices of dealing with these log files, on both Spark on YARN and standalone mode.

Log4j’s RollingFileAppender

Spark uses log4j as logging facility. The default configuraiton is to write all logs into standard error, which is fine for batch jobs. But for streaming jobs, we’d better use rolling-file appender, to cut log files by size and keep only several recent files. Here’s an example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
log4j.rootLogger=INFO, rolling
log4j.appender.rolling=org.apache.log4j.RollingFileAppender
log4j.appender.rolling.layout=org.apache.log4j.PatternLayout
log4j.appender.rolling.layout.conversionPattern=[%d] %p %m (%c)%n
log4j.appender.rolling.maxFileSize=50MB
log4j.appender.rolling.maxBackupIndex=5
log4j.appender.rolling.file=/var/log/spark/${dm.logging.name}.log
log4j.appender.rolling.encoding=UTF-8
log4j.logger.org.apache.spark=WARN
log4j.logger.org.eclipse.jetty=WARN
log4j.logger.com.anjuke.dm=${dm.logging.level}

This means log4j will roll the log file by 50MB and keep only 5 recent files. These files are saved in /var/log/spark directory, with filename picked from system property dm.logging.name. We also set the logging level of our package com.anjuke.dm according to dm.logging.level property. Another thing to mention is that we set org.apache.spark to level WARN, so as to ignore verbose logs from spark.

Read More

ElasticSearch Performance Tips

Recently we’re using ElasticSearch as a data backend of our recommendation API, to serve both offline and online computed data to users. Thanks to ElasticSearch’s rich and out-of-the-box functionality, it doesn’t take much trouble to setup the cluster. However, we still encounter some misuse and unwise configurations. So here’s a list of ElasticSearch performance tips that we learned from practice.

Tip 1 Set Num-of-shards to Num-of-nodes

Shard is the foundation of ElasticSearch’s distribution capability. Every index is splitted into several shards (default 5) and are distributed across cluster nodes. But this capability does not come free. Since data being queried reside in all shards (this behaviour can be changed by routing), ElasticSearch has to run this query on every shard, fetch the result, and merge them, like a map-reduce process. So if there’re too many shards, more than the number of cluter nodes, the query will be executed more than once on the same node, and it’ll also impact the merge phase. On the other hand, too few shards will also reduce the performance, for not all nodes are being utilized.

Shards have two roles, primary shard and replica shard. Replica shard serves as a backup to the primary shard. When primary goes down, the replica takes its job. It also helps improving the search and get performance, for these requests can be executed on either primary or replica shard.

Shards can be visualized by elasticsearch-head plugin:

The cu_docs index has two shards 0 and 1, with number_of_replicas set to 1. Primary shard 0 (bold bordered) resides in server Leon, and its replica in Pris. They are green becuase all primary shards have enough repicas sitting in different servers, so the cluster is healthy.

Since number_of_shards of an index cannot be changed after creation (while number_of_replicas can), one should choose this config wisely. Here are some suggestions:

  1. How many nodes do you have, now and future? If you’re sure you’ll only have 3 nodes, set number of shards to 2 and replicas to 1, so there’ll be 4 shards across 3 nodes. If you’ll add some servers in the future, you can set number of shards to 3, so when the cluster grows to 5 nodes, there’ll be 6 distributed shards.
  2. How big is your index? If it’s small, one shard with one replica will due.
  3. How is the read and write frequency, respectively? If it’s search heavy, setup more relicas.

Read More

Apache HBase的适用场景

原文:http://blog.cloudera.com/blog/2011/04/hbase-dos-and-donts/

最近我在洛杉矶Hadoop用户组做了一次关于HBase适用场景的分享。在场的听众水平都很高,给到了我很多值得深思的反馈。主办方是来自Shopzilla的Jody,我非常感谢他能给我一个在60多位Hadoop使用者面前演讲的机会。可能一些朋友没有机会来洛杉矶参加这次会议,我将分享中的主要内容做了一个整理。如果你没有时间阅读全文,以下是一些摘要:

  • HBase很棒,但不是关系型数据库或HDFS的替代者;
  • 配置得当才能运行良好;
  • 监控,监控,监控,重要的事情要说三遍。

Cloudera是HBase的铁杆粉丝。我们热爱这项技术,热爱这个社区,发现它能适用于非常多的应用场景。HBase如今已经有很多成功案例,所以很多公司也在考虑如何将其应用到自己的架构中。我做这次分享以及写这篇文章的动因就是希望能列举出HBase的适用场景,并提醒各位哪些场景是不适用的,以及如何做好HBase的部署。

Read More

深入理解Reduce-side Join

在《MapReduce Design Patterns》一书中,作者给出了Reduce-side Join的实现方法,大致步骤如下:

  1. 使用MultipleInputs指定不同的来源表和相应的Mapper类;
  2. Mapper输出的Key为Join的字段内容,Value为打了来源表标签的记录;
  3. Reducer在接收到同一个Key的记录后,执行以下两步:
    1. 遍历Values,根据标签将来源表的记录分别放到两个List中;
    2. 遍历两个List,输出Join结果。

具体实现可以参考这段代码。但是这种实现方法有一个问题:如果同一个Key的记录数过多,存放在List中就会占用很多内存,严重的会造成内存溢出(Out of Memory, OOM)。这种方法在一对一的情况下没有问题,而一对多、多对多的情况就会有隐患。那么,Hive在做Reduce-side Join时是如何避免OOM的呢?两个关键点:

  1. Reducer在遍历Values时,会将前面的表缓存在内存中,对于最后一张表则边扫描边输出;
  2. 如果前面几张表内存中放不下,就写入磁盘。

Read More

使用git rebase让历史变得清晰

当多人协作开发一个分支时,历史记录通常如下方左图所示,比较凌乱。如果希望能像右图那样呈线性提交,就需要学习git rebase的用法。

“Merge branch”提交的产生

我们的工作流程是:修改代码→提交到本地仓库→拉取远程改动→推送。正是在git pull这一步产生的Merge branch提交。事实上,git pull等效于get fetch origin和get merge origin/master这两条命令,前者是拉取远程仓库到本地临时库,后者是将临时库中的改动合并到本地分支中。

要避免Merge branch提交也有一个“土法”:先pull、再commit、最后push。不过万一commit和push之间远程又发生了改动,还需要再pull一次,就又会产生Merge branch提交。

使用git pull –rebase

修改代码→commit→git pull –rebase→git push。也就是将get merge origin/master替换成了git rebase origin/master,它的过程是先将HEAD指向origin/master,然后逐一应用本地的修改,这样就不会产生Merge branch提交了。具体过程见下文扩展阅读。

Read More