跳到主要内容

为什么开源项目能得以完成

· 阅读需 2 分钟

开源项目的开发人员通常是业余的时间在做开发,开发人员和时间都很不稳定,最后却大多都成就了很优秀的作品。 开源项目有哪些亮点,让其能做到这一切呢?

我并无大型项目开发经验,却也读过一些书籍,涉及大型项目协作。软件开发是一个非常依赖知识共享的工作,因此通常而言一个项目组不宜大于 12 人,否则会带来沟通成本急剧上升。于是通常大的项目,通常是拆分成小项目来完成,却又带来对项目全景不了解的问题。 相对于企业中从上而下的强制拆分,我更欣赏我在 Github 上看到的开源软件开发模式。

于是我用心去研究开源项目是如何运作的,并列举其成功原因如下:

  • 优秀的程序员。在软件开发中,人是最重要的。开源项目的开发人员几乎全是最优秀的开发者,有专业的知识体系,有高质量的代码能力,有丰富的经验。同时,开发者也很友善地帮助和支持新人进入项目。
  • 先进的项目管理。开源项目应该是最先重视项目集成/测试能力的。由于开发人员大多是基于兴趣去贡献代码,并无多少项目管理和测试人员,于是很关注如何自动化的测试、集成、部署、发布等。
  • 先进的工具。大多数企业还在使用 CVS,SVN 的时候,开源项目开发者已经率先投入了 Git 的怀抱。而 Github 是软件项目协作的先行者,提供了 Git 仓储、Issue、PR 功能。这些功能为开源项目提供了极大的便利性。

2016-09-10

· 阅读需 1 分钟

今天,定下了我的技术上的原则宗旨。 以及,未来 1~2 年内的目标。

一个原则

最大化开发者的生产力

三个目标

  1. 掌握并优化现有 coding 项目,成为核心开发者。
  2. 跟上时代之巅,跟随最先进的开发者和开发方式。
  3. 参与开源项目。

关于Java,对象化,Spring/Bean的一些随想

· 阅读需 3 分钟

最近在全力学习什么叫做Java,什么叫做Spring,什么是Bean。随便写写想法。

名词王国

首先介绍这篇文章名词王国里的死刑(翻译),就尽了全力在嘲讽Java的唯对象化编程的。 在学习Java的过程中,特别是现在在看一个不那么成熟,但已经比较庞大的项目,更加是心有同感。 这是一切的前提。 比如一个Restful处理OAuth的api,需要有一个OAuthController, OAuthProvider, OAuthService, OAuthConfig..... 心累。

松耦合

Java的问题还不止名词滥用一个。 另外一个问题是,耦合性过高的问题。为了解决这个问题,Java竭尽脑汁。 出了Interface,这样,类之间就不那么需要互相依赖了。 然而,还是需要有一个主控来调用他们,赋值使用的。 最后是Spring出来统一了天下。

Interface vs Duck Test

其实Interface和Duck Test(鸭子方法)是一回事。 鸭子方法解释为:『如果它看起来像鸭子,游泳像鸭子,叫声像鸭子,那么它可能就是只鸭子。』 Interface就是在事先说好了鸭子应该长得像什么,然后有这些功能的都可以扮演鸭子。 不知道所谓的AOP编程是不是指这个呢?存疑。

Spring and Bean

看了一篇博客,觉得是学Bean最好的方法。 简单说就是自己最小化的模拟着做了一个Spring的Bean系统,而且还是最新的基于注解(annotation)的。 我把它给实现并上传到github了。 简单说,Spring自己有一个注册库。所有注解为Bean的,就是在里面注册了可以用的一个创建实例的方法。 用@Autowired/@Resource/@Qualifier的,是由Spring控制着往里面装载实例的地方。 以此达到上述的松耦合。 但是实际上,实现的方式无非是通过『字符串』或者『类』,归根结底是采用了动态语言的性质。

java.lang.reflect vs meta programming

有一段时间,Ruby的元编程火到到处都是。 其实Java的反射也是一回事,只是废话比较多,语言比较臃肿。 现在最新的注解,其实是依赖于反射的。

Java vs JavaScript

实话实说,做JavaEE,感觉还不如用JavaScript写。 当然,写nodejs其实对人的要求更高。 但是JavaEE好臃肿,代码冗长,体积臃肿。 相对比,我觉得Java是在细节和底层做了许多优化,比如『静态语言』这一项也看做是优化,而nodejs则是底层随便写。 然而底层高效的Java如果不好好写顶端的话,就会变得很搞笑了。 我想写Nodejs。

使用端口映射帮助开发web服务

· 阅读需 2 分钟

问题

当前在开发微信公众号的开发服务器,遇到一个问题是:微信希望我的url是某安全域名下的某80端口。 而我的开发设备在某内网,80端口还被长城宽带封了。 怎么办?

当然我肯定有远端的开发服务器的。 我之前的做法是:修改代码,上传至远端,部署重启,debug。 效率十分十分十分慢。

然而实际上,非常非常简单就能完成远端80端口代理至本机的3000端口。

解决方案

我主要是通过此文学习的。

总而言之

ssh -NR 80:127.0.0.1:3000 root@dev.server.com

就可以了。

这时候,访问http://dev.server.com/ 就相当于直接访问http://127.0.0.1:3000

nginx

当然了,上述要求服务器的80端口不被占用。 如果有多人使用,或者服务器有80端口使用的情况下。 通过nginx转发也能轻松实现web开发环境配置。

server {
listen 80 default_server;
listen [::]:80 default_server ipv6only=on;
client_max_body_size 10M;
client_body_buffer_size 128k;

server_name dev.server.com;

index index.html;

location / {
root /website;
}
location ~ /a/ {
rewrite /a/(.*)$ /$1 break;
proxy_pass http://localhost:3000;
}
location ~ /b/ {
rewrite /b/(.*)$ /$1 break;
proxy_pass http://localhost:3031;
}
}

这样子的配置 http://dev.server.com/a/ 就相当于直接访问http://127.0.0.1:3000http://dev.server.com/b/ 就相当于直接访问http://127.0.0.1:3031

微信Oauth2授权

· 阅读需 2 分钟

本文是我个人对oauth2的理解。

可以参考阮一峰的理解OAuth 2.0

其实微信采取的Oauth2策略是最复杂的“授权码模式”。

步奏是这样的:

  1. 我们的app跟用户索要授权。
  2. 用户跟微信授权服务器说这件事,微信返回一个code。(微信调用微信内核鉴权,并不需要输入用户名密码。其实像qq的oauth需要通过输入用户名密码登录,那是不对的。因为大部分用户并无法分辨要求登录的页面是钓鱼页面还是https的qq登录页面)
  3. 用户把code告诉我们的服务器。
  4. 我们的服务器跟微信的服务器对code,同时提供预先在微信设置的应用密码(POST加密传输)。服务器通过应用密码确定我们身份,通过code确定用户授权给我们了。如拿回微信服务器给我们一个用户的access token和一个refresh token。(通常access token存活时间为2小时,refresh token为30天。主要原因是access token会一直带着?)
  5. access token拿去用。

我们在这基础上加上自己的jwt(json web token)机制。jwt其实是一种session的升级,用localstorage取代session使用的cookies。 思路如下:

  1. 用户以jwt对我们服务器发起api请求。(有无效可以最简单只通过时间过期,复杂的检测refresh token有效性)无效跳转至6
  2. jwt有效,正常运行。 有需要获取微信的资源的,检测access token有效性。无效跳去4
  3. 正常获取。
  4. access token 无效, 通过refresh token更新access token。成功则跳去3
  5. 如果refresh token无效,需要重新获取refresh token,则重新进行微信oauth2
  6. 通过上述微信oauth2获得用户的openid(跟access token一起返回),access token, refresh token。以openid为主key,储存access token, refresh token。
  7. 以openid为key,生成jwt,设置合理时间,并跳转至2

开发服务器是干嘛的

· 阅读需 7 分钟

上网搜了半天也没找到“开发服务器”是用来干嘛的,我就按照自己的经验讲一讲好了。

代码(文件)仓储

开发服务器最重要的功能是提供仓储功能。我们用的最多的是代码仓储。 在CVS年代,甚至到SVN的年代,许多中小型开发团队都会配备开发服务器,然后写的代码没问题了,提交上去,ok收工。 然后CVS和SVN对文本文件和二进制文件的口味比较不挑(我感觉的,git就比较区分两种文件了,而且一般希望不处理二进制文件),会把编译好的安装包,服务器打包文件,文档(doc+jpg),甚至连一些需要预安装的库和程序也打包进来,仓储起来。

GIT

我们现在更多的是git。git关注于文本文件的变化,它的观察是以“行”为单位的。 通常我不希望在我们的git仓储中带上二进制文件如:doc、jpg、png、gif、mp3、avi等,甚至是pdf。(希望用html或者md来代替) 我们使用针对GIT的SAAS就好了,比如coding.net的git服务。

开发测试

API

比如我们的API服务器。

编译、运行

c++什么的,编译一个程序跑20分钟很正常,在自己机子上跑吗?太浪费时间了。 搞一个cpu、mem很强的服务器,把代码提交上去,跑编译。对,20分钟是服务器上跑的时间。 运行,搞个虚拟机来跑跑程序试一下。(我们一般不需要,装一个Firefox就够了。当然如果需要兼容ie,我就把这个任务交给你们这群使用windows的吧!)

Trace系统

就是bug追踪什么的。 像我们发任务啊,以前发任务不用这个,但是他们有工作汇报提交(windows共享)。 说到文件共享,我们原来公司的切图仔(美工的昵称)会切好图,然后扔到windows共享里,然后大吼一声(或者QQ一抖):文件放在xxx文件夹里。 还有一些公司运维相关也会被扔到开发服务器里来。像公司邮箱。

总结

在在线服务不成熟、网络慢、协作开发平台几近无的10年前,没有一个开发服务器(一般是本地)的,都不好意思叫研发团队! 他们也会备份,中小型团队一般每几个月拿个硬盘插到服务器上,拷贝一下。

远程开发服务器

比如我们的aliyun服务器。

优点

远程开发服务器的优势在于远程! 一般他们会叫做“云服务器”,其实就是自己的一个云主机。 托管了宽带、cpu、内存、用电。性价比高、稳定、安全。 文件丢失的可能性降低到原来模式的万分之一。

缺点

唯一不爽的就是占用公网宽带。相对的带来好处是哪里开发都一样。

干什么

上面说的开发服务器该做的一切事情,都可以用远程开发服务器来做。

代码仓储服务 && 协作服务

如github、如coding.net。 可以在我们的开发服务器中剥离这些功能。 毕竟部署和维护这些服务还是挺累人的。

离岸工作

其实不管是git仓储、coding.net的协作工具、aliyun的云开发服务器。 我们的配置都是往分布式开发方向靠近的。 否则的话,我们就上本地svn仓储、trac (wikipedia)、本地开发服务器了。(我真这样做过,trac系统svn本地服务器什么的,不过只有我一个人用着玩)

我觉得离岸工作,将是以后的趋势。 毕竟网络会越来越快的,沟通会越来越方便((全息)视频聊天会议),但是出行会越来越堵。

先人一步,早点学习,总好过追着吃尘。

本地开发服务器

啊,那还本地服务器什么,既然离岸开发都是趋势了?!

首先

首先,是趋势但是还没到,现在网还很值钱,而且就算是以后要离岸,也得先锻炼个2~4年才靠谱啊! 比如通过结对编程来锻炼,那得我在这边写,你在那边同步的看,并且可以聊天说说我哪里没写对。 而且人是需要有社会交流的,码农们你们不靠上班见见人,锻炼一下交际能力,以后怎么混社会啊?! 而且也不是真的所有人都互相不见面的。总而言之,以后部分项目组会通过离岸开发,部分会要求定期见面,还有的会要挤在一起写代码。 现在就已经存在这三类项目组了,会变的只是存在的比例而已。 而且那些个完全不需要见面的项目组,快结束的时候总是会想聚在一次见个面吃个饭什么的。

其次

其次,不要说公司了,在家庭里,甚至只有我一个人,如果经济允许,那我也要搞服务器的啊!比如家庭服务器啊。 强调便携性的笔记本,性能永远会是一个坎,唯一的解决办法就是做图像投射,也就是用类似teamviewer的软件,操作另外一个系统。当网络相应速度(UDP)降低到1ms以下且带宽大于2GB/s时,这就会发生,笔记本主要变成了输入输出设备了!这个时候,另外那个系统不就是本地服务器嘛?!(现在可以考虑做便携式输入输出设备兼超高速无线网络连接的专利了) 好,不说这么远。家庭服务器没事挂了bt下载,到家了用upnp转发一下视频到电视机(投影仪)上,不也是绝佳的体验吗?

正经说

那本地开发服务器相对远程开发服务器有什么优势呢? 对,上面说了,只有网络问题。

  1. 公网请求,速度最少100ms左右吧。你ping一下百度。在ping一下我们的本地服务器。看看差别。
  2. 带宽:下行一般还好,如果对方不设限,快的时候可以到2.5MB/s,但是上传,别提了100KB/s已经感谢天感谢地,还得感谢同事没有嫌你卡着网。

如果千兆带宽不设限,那不用往下看了,我们就用远程吧。

本地开发服务器可用作这几个功能:

  1. 加速服务: 如果有经常性,集中,大量的下载需求,而且网并不快的话,可以考虑加速服务,比如我们的npm加速。
  2. 文件仓储: 如果文件仓储很大,而且网特别是上行并不快的话,可以考虑做本地文件仓储。不过我们的npm仓储并不作为例子,因为真的要实现的话,我会上aliyun。
  3. 开发测试:如果网并不快的话,可以开发的时候,多考虑使用本地开发服务器。
  4. 文件共享:似乎用不上,我研究下开了方便一下大家吧。

性能

我们暂时不需要强的服务器,对cpu、mem、硬盘的要求都几近无。 不过我已经看上某宝上某二手服务器主板DIY之后的产品了。 做个广告。(不是我的店,只是好想买) 你们如果要弄游戏主机的话,可以考虑这样搞,除了耗电高,发热大,开机慢,没有任何不好的。

构建npm镜像源

· 阅读需 3 分钟

其实有淘宝npm镜像个人是完全无必要做npm的镜像的。 一般的研发团队的宽带也无须考虑。但是我们是长城宽带。 正好公司里有一台没啥事做的服务器,决定上手搞一个npm的镜像。

原因

  1. npm安装的时间很长,同时依赖于公司带宽。
  2. 大规模/大量前端项目,可以把公用的模块独立成npm模块,单独管理。所以需要私有的npm仓储。

解决方案

我们暂时不需要第二点,故使用local-npm解决npm加速问题就好了。

以后可以考虑换用cnpmjs来做私有仓储。

fullfatdb

fullfat就是全脂的意思,主要相对下面skim脱脂而言的。 我们使用淘宝的fullfatdb: https://registry.npm.taobao.org/ 用的是官网的,因为local-npm对cnpmjs的镜像不兼容。给local-npm发了个issue,暂时没空去贡献代码。

当前 registry.npm.taobao.org 是从 r.cnpmjs.org 进行全量同步的 Our public registry: r.cnpmjs.org, syncing from registry.npmjs.org

skimdb

skim是脱脂的意思。去掉了上面fullfatdb中二进制的部分,应该只是metadata部分,大概0.95GB大小。 使用官方的 https://skimdb.npmjs.com/registry 进行克隆。因为暂时好像还没有国内镜像源。

部署

服务部署在本地开发服务器上。

npm install -g local-npm
mkdir npm
cd npm
#local-npm -r https://registry.npm.taobao.org #不能使用-r taobao了,因为taobao是cnpmjs架设的,而-r只支持local-npm或者源站。
local-npm -u http://192.168.174.233:5080 #必须用上-u,否则会败,如果已经败了,在client上记得跑`npm cache clean`清理一下。

使用

不管用不用本地的npm服务源,都推荐用nrm进行npm源控制。就像不管你用不用多版本node,都推荐你使用nvm一样。

nrm

安装npm install -g nrm,使用很简单。 可以有

nrm test
nrm use taobao

其实不用test直接use就好了。 你可以直接输入nrm看看所有命令。 其中有一个比较有趣的nrm home taobao

local-dev

[local-dev] 用 192.168.174.233 代替。 (公司内部服务器地址)

使用nrm添加新源:

nrm add local http://[local-dev]:5080 http://[local-dev]:5080/_browse
nrm use local

就可以了。 现在你可以nrm test看看谁延迟更低。

对,如果你注意到了的话,http://[local-dev]:5080/_browse 是我们的一个npm搜索引擎。 可以通过

nrm home local

查看。

注意

由于只同步了0.95GB的metadata,二进制包并未同步。第一次下载模块时可能比平时慢(二进制包由server从官网下载,比淘宝要慢;当然查metadata还是块一些的) 第二次就快了。

代码仓储(git vs svn)

· 阅读需 7 分钟

个人感觉git比起svn有着太多的优势了。 本文议论一下git和svn,以及团队协作的开发模式。

SVN开发模式

在CVS年代,甚至到SVN的年代,许多中小型开发团队的开发流程是这样的: 这个说得挺清楚的。 一般有两种开发方式。

集中式开发

  1. 开始一天新的开发工作,打开电脑,从trunk中同步所有代码到本地。
  2. 改改改。
  3. 差不多1个小时了,这个过程没写完,先这样吧能编译就行,开始提交版本。
  4. 同步trunk到本地,解决conflict,上传。
  5. 看看离下班还有多久,大于30分钟则去2。
  6. 晃到下班走人。

分散式开发

  1. 新功能开发,分配任务。分配到任务qqq。
  2. 创建从trunk中检出(checkout)所有文件到本地;创建branches/dev_1.1.2_qqq文件夹,提交所有文件到该文件夹(于是创建了分支,是copy过去的)。
  3. 改改改。
  4. 提交到branches/dev_1.1.2_qqq。
  5. 完成qqq了吗?没有则跳转到3。
  6. 合并branches/dev_1.1.2_qqq到trunk,解决所有冲突问题。
  7. 中途随时下班可以upload所有东西到branches/dev_1.1.2_qqq,随时上班可以从branches/dev_1.1.2_qqq同步所有东西下来。

svn与git的区别

感觉其实跟使用git差不了太多是吧? 看看这篇my-git

我觉得其中最大的区别是:

  1. 内容控制git是以“行”为单位的,更关注变化;而svn是以文件为单位的,更关注区别;所以svn更容易产生冲突(好像就没办法auto merge好么!)。
  2. git(和hg)是分布式仓储,而svn(和cvs)是集中式的。什么意思呢?那是svn所有的提交检出操作,都是联网进行的,都是跟服务器通信的,没有本地仓储。
  3. 速度。

冲突

你看看刚刚这个博客里怎么写的:

如何降低冲突解决的复杂度: 1、当文档编辑完成后,尽快提交,频繁的提交/更新可以降低在冲突发生的概率,以及发生时解决冲突的复杂度。 2、在提交时,写上明确的message,方便以后查找用户更新的原因,毕竟随着时间的推移,对当初更新的原因有可能会遗忘 3、养成良好的使用习惯,使用SVN时每次都是先提交,后更新。每天早上打开后,首先要从版本库获取最新版本。每天下班前必须将已经编辑过的文档都提交到版本库。

就是提交、提交、提交!更新、更新、更新! 很容易就改同一个文件好不好! 只要改同一个文件就是冲突! 是冲突就得自己处理!

注意

不过实际上,git merge的时候,如果同样两个文件被修改,即使合并成功了,也应该自己评审一遍。 因为自动合并是会出问题的,比如由苹果的低级Bug想到的这篇文章。 自动合并是基于文本(行)而不是基于语法,所以有可能发生上述问题。

不管怎样,自动合并简直就是神器!!快谢谢它。

集中式 vs 分布式 仓储

git在刚刚学习的时候觉得很奇怪,working directory是什么啊,local repository是什么啊,checkout 和 pull 还有merge又是什么鬼! 的确这个比svn的commit/update复杂一点点,那是因为这相当于在本地架设了一个svn服务器,并提供了简单灵活的同步管理工具好不好!

有人觉得集中式仓储还是有好处的,比如代码安全性,防盗啊! 难道svn就不能盗走了吗?我有朋友的亲戚公司的代码被程序员删了走人的。离职前把代码拷走更是正常不过。 说真的,防抄袭这种事情只能靠程序员的个人职业操守、和签署的非竞协议。

本地分支啊,多爽啊!

对,还没说到重点

重点

重点是,一旦连不上svn服务器你就傻逼了。在公司万一svn服务器一卡一断网,就可以喝咖啡放假了。更别提什么回家写、咖啡厅图书馆写、边做火车边写什么的。 你checkout一个代码,等通过svn服务器;提交一个代码,得通过svn服务器;查一个日志,得通过svn服务器;对比一个改动,得通过svn服务器。 而且,就算在公司局域网里,svn操作也非、常、慢!

集中式 vs 分散式 开发

不管是git还是svn,都有集中式和分散式开发方式的区别。(其实这里分散式和分布式是一个意思,我沿用某篇引用博客的说法,而他使用这个说法估计是为了不要引起误解)

我是强烈推荐分散式开发的,正如我强烈推荐分布式代码管理一样。 当然我不认为“各自开发3个月,然后凑在一起试一下,然后合并代码”是对的。 分散式开发中,一个临时开发分支的生命周期应该是几小时至几天不等。如果一个临时开发分支需要存活十几天甚至一两个月的话,考虑拆分任务。

正因为分散式开发对分支高度的需求,使得分布式仓储对比集中式仓储有着极大的优势。

速度

git的速度大大大于svn的速度,具体原因涉及其具体实现,不解释。 git支持本地文件、ssh、git、http四种协议,口味很广。

二进制

我们现在更多的是git。git关注于文本文件的变化,它的观察是以“行”为单位的。 通常我不希望在我们的git仓储中带上二进制文件如:doc、jpg、png、gif、mp3、avi等,甚至是pdf。(希望用html或者md来代替) CVS和SVN对文本文件和二进制文件的口味比较不挑,经常研发经理会把编译好的安装包,服务器打包文件,文档(doc+jpg),甚至连一些需要预安装的库和程序也打包进来,仓储起来。 似乎是因为git不管你二进制的东西是什么,它都会帮你压缩一遍,而像刚刚说的jpg、mp3,还有一些个zip、mkv等文件是压不了多少空间的,压缩反倒浪费时间。 这里涉及其设计哲学。git设计者认为你就不应该往这里扔大的文件,不是我不对,是你不会用。git主要面向文本文件(最好是源码)、以及小文件(比如小于10kb)。 而二进制文件,你不压怎么知道压不压地动呢?那些个压不动的文件之所以压不动,那是因为他们已经被压过了。 压过的文件你就更不应该往git里传了。

似乎这里svn还有点用,可以用来放文档、压缩包、安装包(安装包也自动被压过的)啊! 但是lfs一出,就没svn什么事了,除非那些懒得学lfs的。(我有空研究一下lfs)

适用性

基于文本文件的项目,都应该考虑git。 git更强于移动开发。 强于大规模项目。 强于超小型项目。

svn只适用于中小型项目组,而且他们懒得学git的。 或者设计师什么的,而且他们的作品都是二进制的那种。

而且svn还得自己买研发服务器,否则网一卡你就哭。

构建运行脚本的微服务

· 阅读需 4 分钟

女朋友正在学习python,写的脚本都是从stdin输入,从stdout输出的;而我又是一个js全栈,感觉写一个简单的python脚本调用的界面+nodejs微服务应该很有意思。

#demo http://ali.wusisu.com/sun/

child_process

首先我要先实现在nodejs中调用python,并且要转发输入输出流。

'use strict'
var child_process = require('child_process')
var cp = child_process.spawn('python3', ['./files/circumference.py'])
cp.stdout.pipe(process.stdout)
process.stdin.pipe(cp.stdin)
cp.on('close', (code) => {
process.exit(code)
})

做的事情简单清晰易懂:

  1. 用child_process的spawn调用一个python3进程cp。
  2. 转发cp的标准输出到当前程序的标准输出(命令行)。
  3. 转发当前程序的标准输入到cp的标准输入。
  4. 监听cp的结束事件。

结果是调用这个node wrapper.js跟直接调用python3 ./files/circumference.py没有任何区别。

曲折

事实上开发的时候走了点弯路的。 我很幼稚的认为,使用Content-Type: text/event-stream能够解决问题。 只要client端发起一个POST请求,但不马上结束连接,而是持续的发送内容;server端使用event-stream持续地返回内容。 事实上我已经把模拟的client端和server端写好了,也完全达到了目标效果,然并卵,在js里没法用ajax慢慢地发POST请求。

event-stream: 告诉client/browser服务器会慢慢地返回内容,你收到多少直接拿去用就好。

socket.io

socket.io也并没有什么需要说的了,就简单推荐一下,好用。

websocket

在socket.io之前我考虑过直接上websocket,因为之前用event-stream实现的时候并没有使用第三方的包,直接用原生的http然后把streampipe出去可爽了。于是就想着原生上websocket。 nodejs的文档其实提到了一下websocket的。文中让我响应upgrade事件然后可以拿到一个socket然后可以pipe给它自己。 然后就没有更多关于怎么发起一个websocket的资讯了。然而在google/baidu等搜索引擎中,并未能找到关于websocket足够给我用的实践,全都在不清不楚地大概说websocket有什么好处,像什么样子。(如果以后有空,自己实践一下,再上文章) 反倒是有了新的东西,在要求upgrade的时候要带一个base64的key,然后在服务器response的时候要给另外一个encrypted的base64。然后还不说这个key是干什么的,简直就是各种不清不楚。

文件遍历/Promise

之前习惯用stage-2async来写同步执行。不过现在babel还没有全面进入node-v5.7.1,要用async需要引入许多babel包和配置,正好前面都做比较原生的事情,于是想着用原生来写。 首先考虑了callback写法,遍历./files目录后调用回调。结果发现Promise.all还是做了不少事情,把这个写到我的递归函数中还是比较难看的,于是还是上Promise吧。

var fs = require('fs')
var lookForwardFiles = function(path){
return new Promise((res,rej)=>{
fs.stat(path, (err, stat)=>{
if(err) return res([])
if(!stat.isDirectory()) return res([path])
fs.readdir(path, (err, files)=>{
if(err) return res([])
var allP = files.map(f=>lookForwardFiles(path + '/' + f))
Promise.all(allP).then(data=>res([].concat.apply([],data)))
})
})
})
}

node支持了arrow functions还是蛮爽的,就是用=>来代替'function'。 这里用到了一个技巧[].concat.apply([],data),可以把形如[[1,2],[3],[4],5]的data转化为[1,2,3,4,5]。 其实也可以用

data.reduce((all,d)=>all.concat(d),[])

来代替。

commit

给个当前commit的链接,万一以后不用socket.io上原生websocket了呢。

nodejs日志控件的选择

· 阅读需 2 分钟

做服务器开发,必不可少的一个模块是日志系统。 在Java界,有大牛log4j,感觉都可以毫无顾虑地直接选择这个模块。但是在nodejs就没有这样事实上的标准了。 于是我花了些时间研究哪个nodejs日志模块好用。

结论

最后我选中的日志插件是

  • bunyan: a simple and fast JSON logging module for node.js services
  • morgan: HTTP request logger middleware for node.js

评价

morgan

morgan是expressjs默认带着的超简单的日志系统,专门用来生成http的请求记录。其生成的日志个人感觉于Nginx的日志十分类似。用来产生一个http请求日志再好不过了。 使用非常简单:

import morgan from 'koa-morgan'
export const loggerMiddleware = morgan('combined')
app.use(loggerMiddleware)

我这里用的是koa2,所以用封装过的morgan来写。

bunyan

bunyan是一个会产生JSON格式的日志的日志模块。 其实可读性不高。其提供了把json转成易读性高日志的阅读工具,但是总觉得不太实用。 不过当我们把它作为一种记载重要操作的操作记录工具(这属于业务功能而不是技术功能了)时,就挺合适使用了。

const Writable = require('stream').Writable
const writeStream = new Writable({ objectMode: true })
const DEBUG = process.env.DEBUG
writeStream._write = function(log, encoding, next){
if(log.level>40) console.error(log)
if (DEBUG && log.level<=40) {
console.log(log)
}
if (log.level<30) return
let item = new Model
item.obj = log
item.save(next)
}
import bunyan from 'bunyan'
export const logger = bunyan.createLogger({
name:'app',
src: true,
streams: [{
level: 'trace',
type: 'raw',
stream: writeStream
}]
})

写入一个记录的方式是:

logger.info({message:'hello world!'})

这里用的比较复杂。本来bunyan的那个streams直接放console或者文件流是非常简明的,在这里我创建了一个writeStream来接受数据流,然后再写一个回调来把数据流存入mongodb中。这样的用例在业务上是非常常见的。