Jaycee's Blog

Keep on hacking.

我这两年的程序人生路

| Comments

一篇总结

为自己写一篇从事程序员以来的生活的总结, 写写这一路的酸甜苦辣.

我叛逆

我是个叛逆心过重的同学, 从小就不喜欢墨守一切大人们告诉我的陈规.

小学初中高中, 也许是这么多年的填鸭式教育压得过久吧, 可能压坏了脑子里某根神经, 在大学在读的末段时间, 2011年的7月, 经过跟自己一个月的神交, 我毅然决然的离开了象牙塔. 至于为什么, 答案是: 这里没有我想要的, 没有发现能让我真正迸发出激情想要去热爱的事物. 说简洁点, 因为不快乐.

我很幸运

离开学校后, 不出意外的迷茫了一段时间, 不知道自己应该做什么, 或者真的喜欢什么. 家庭条件还可以, 找个工作不是什么难事, 可是那样我就会开心了么, 从大学退学就为了这个? Of course not!

2011年9月, 一次偶然的机会, 我来北京看望朋友, 他那时在北京已经工作了半年左右, 在一家创业公司做后端开发. 看见他经常津津有味的在键盘上飞舞手指, 一点也搞不懂他在做什么. 只看见大段五颜六色的代码不一会儿就飞流直下了三千尺. 也是他, 在后来的几天, 告诉了我世上其实还有一个这样适合于我的工作.

是的, 我就是这么狗血的, 义无反顾留在了北京. 我开始对知识如饥似渴, 每天开心的学习一切能让我成为一名程序员的事物.

一年的蛰伏

所以, 你以为我就像电视剧里那样, 在下一集就成为武林高手了? 刚开始的时候确是有过那样的幻觉, 可是很快的我就发现, 我不是虚竹, 我不会武功, 我只能熬夜, 拼命啃书.整整一年的时间, 边学习, 边找工作. 而对于找工作, 我想我比任何刚从大学出来的童学都要有发言权.

那一年, 我面试了不下50家公司, 其中有一家做java的外包公司要了我, 起初我很兴奋, 以为自己的梦想开始了. 但是, 很快我就发现那个公司很不靠谱, 说白了他们就是在卖人, 培训, 送你到别的公司做项目, 做好了留下, 做不好回来接着训, 没有人给你讲真正应该如何去学习, 没有人告诉你应该怎样去成为一个优秀的程序员, 难道这就是我一直追求的么, 我觉得自己每天都在被扼杀想象力, 所以不出两个月, 我就辞职了.

其后的一些公司, 也确实有我理想中的那种, 氛围好. 一个好公司, 给人的第一感觉就会是震撼的, 员工的状态, 互相之间的交流, 让你从汗毛孔都能感受到那份对工作的专注热爱. 但是很可惜, 我都被拒了, 理由是非专业, 没有经验, 基础差, 即使我开出的工资从始自终都是是0 RMB. 这也是意料之中的事, 我所能做的就是在每次求职失败以后, 找出自己的不足, 然后补上.

值得一提的是, 在这端期间, 我学会了翻墙, 而且被彻底从微软的牢笼中解放了. linux为我开启了程序世界的第一扇门, 我发自内心的喜欢上了类unix设计的操作系统, ubuntu, centOS, freeBSD. 以至于我后来下了血本买了现在使用着的macbook pro. 直到现在我也认为, 它是我活到现在为止买过的最物超所值的东西.

这段时间对我来说最宝贵的, 是那份经历了无数次打击, 却从仍然不放弃的往前冲的坚韧.

从门外到门内

后来的2012年下半年, 通过朋友辗转的推荐, 我得到了第一份真正意义上的工作. 这是一家创业公司, 算我在内技术6人, 由于没有经验, 让我先去做运维, 从基础学起, 像脚本的编写, 工具的安装设置部署, linux基本操作. 新工作的工作量很大, 而这正合我意, 因为我深知, 最好的学习就是在生产环境中去实践.

当时带着我的前辈是个经验非常丰富的老师傅, 很会带人, 起初因为我基础差, 一些东西会手把手的教我. 当发觉我大概知道如何去学习了之后, 就会偶尔才点播一两句, 以免让我对他产生依赖性, 不愿意思考. 托他的福, 我进步的很快, 渐渐的对当前的工作开始不满足, 于是主动要求公司我一些力所能及的其他的工作, 当时的自己, 还处于对于那些完全没有接触过的技术会产生一些恐惧的状态, 可我也明白, 不去亲自动手写, 就永远学不会.

那时的项目源码的主体架构是已经搭好了的, 我需要做的就是开发数据接口, 服务器端技术, 用的是一位国人大牛基于nginx内核开发的很新的框架 — openresty. 虽然平时的工作很简单, 只是用lua开发一些数据接口(openresty是用lua作为逻辑层语言的, 是个很优秀的框架, 很适合处理高并发, 感兴趣的朋友可以google一下 : P), 但通过对其的使用, 我恶补到了很多自己基础知识的死角(好吧, 其实那时基本上全是死角), 最棒的是, 我找到了适合自己的学习方法.

很感谢那段时间, 让我学到了一个产品完成的全过程, 从设计到开发, 项目的流程管理, 上线, 推广. 期间还推倒重做过2次, 因为人少, 基本上一天干人家三天的活儿, 每周六天班, 早10晚10连轴转. 到后来, 公司也渐渐开始信任我, 让我涉猎的部分也就越来越多, 每天的感觉都很棒, 就像开足了马力的发动机, 日进千里的.

这段时间总结为一句话: 很辛苦, 很充实, 很快乐, 终于算是入门了.

之后

之后的一段时间, 发生了很多事, 算是我来京之后最不顺利的一段时间.

首先是出于对未来发展的考虑, 也因为一些工作环境的限制, 我从在职的那家公司辞了职. 虽然有过了工作经验, 技术能力上有了较门外汉而言质的飞跃, 可不代表这就够了. 放眼望去, 我不过也是偌大的北京城里, 一个刚入门的菜鸟级程序员. 于是我开始了新一轮的找工作之旅.

虽然较以前有了一定的自信, 但是事情没有我想的那么顺利, 加上当时并不是一个求职的高峰期, 我一直没有找到想要的那种公司. 也是在这段时间, 我结束了一段一年的感情和同居生活, 从两个人奋斗, 变成了一个人奔波.

求职的压力, 感情的不顺利, 甚至还有家人的不理解. 我开始有些沮丧, 有些怀疑自己了. 可是沮丧是解决不了问题的! 我决定给自己放个小假, 也想家了, 想回去看看大海.

对于不顺的经历, 不愿意说的太多了, 也实在有些懒得去仔细回忆了, 过去了就让它过去吧. 于是, 2013年3月, 我回到了家乡大连.

面朝大海, 春暖花开

回家的每一天, 都是温暖的, 白天在家看看书写写代码, 晚上偶尔去海边散散步, 跟朋友喝一杯, 心情纾解了很多, 很多事也想开了很多, 慢慢的放下了心中的浮躁和焦虑, 路是要靠脚踏实地走的, 我开始更踏实的打基础, 看操作系统, 数据结构和算法, 看新奇好玩的技术.

就这样, 我度过了充实的两2个月. 调整好了身心, 整装再出发.

当你纠结痛苦的时候, 不妨给自己放个假, 做些喜欢做的事, 从那个怪圈中抽离出来, 不失为一种好的调节. 当我再次回到北京后, 一切似乎都顺利了.

重新上路

2013年5月, 回北京后的第一个礼拜, 我就找到了工作. 一家做教育的互联网公司. 当时做线上教育的公司其实不少, 但他们的公司文化让我很有共鸣, 公司里人人平等, 没有太多的约束, 每个人在完成手头工作以后, 可以自由支配时间和精力. 也是在这里, 我接触了node.js这门日后让我痴迷的技术.

随后的日子, 是一个真正意义上的飞跃, 进入公司后, 因为人手的关系, 我需要独挑两个后端大梁, 客户端, 网站一个, 从框架编写搭建, api设计制定, 功能模块选择, 事务处理, 测试, 部署都要亲力亲为, 对我来说, 是个不小的挑战, 但是我很兴奋.

两个后端全部都是用node.js做服务器, 用的多了, 我逐渐发觉这是门多么优秀的技术, 也为javascript能在后端展现如此能量所惊艳. 每天忙完公司的业务, 以及业余时间所看所学的全部都是node.js相关的技术, 我再一次如饥似渴, 并且渴望交流.

于是10月, 11月, 我去参加许多的技术party, 其中印象最深的是GDG举办的GDC(顺便一提, GDC的午餐真不是盖的啊!中西自助啊!)和今年在北京举办的js盛会京JS. 两个活动中, 跟很多同行探讨技术, 学习知识, 动手实践, 感觉受益良多, 其中印象最深的一场演说, 是京JS上, npm 模块达人substack的一场精彩的现场演示, 娴熟的vim操作, 清晰的逻辑思路, 分分钟编写发布npm模块, 让人觉得是种享受. 最终要的收获, 是思想. 产品设计的模块化思想, 其实早已是老生常谈了, 但是node.js特色的模块化设计, 让模块化真正意义上的变成了模块化式开发, node.js的社区正在飞速的蓬勃发展, 而我也是其中的一份子, 这一切都太美好了.

至今, 我仍在这家公司工作, 但过完年就可能要离开了, 有了换工作的打算是因为最近到了一个瓶颈期, 而按公司的现况短期内不会有太大的改变, 新品即将要上线了, 主要经历也已经开始专注于推广, 可我还想要向更高去挑战.

技术

最后顺便也聊聊自己使用过的技术吧, 程序员嘛, 总是和技术如影随形.

先从语言说起好了, 这两年也接触过大大小小7, 8门语言, ruby, python, lua, js, oc, c, c++, php, lisp, go. 其中除了lua和js, 基本上都属于玩票性质的简单玩过, 我个人偏爱的是ruby和go, 都很优秀, 写起来有种被释放了手脚的酣畅感, 去掉了繁琐的语法上的细枝末节, 让程序员专注于业务逻辑, .因为是玩票的, 所以就不多做评论, 只表达个喜爱: )

接下来说说我熟悉的, 上文说过我早期是写lua的, 其实lua是们非常好的语言, 跟js很像, 而且它是脚本语言里最快的. 它是门寄生式的语言, 可以轻松嵌入到c和c++中去做扩展. node.js早期设计选择语言的时候, 它也一度是最接近作者需求的语言, 后来因为标准库非异步而惨遭淘汰. lua的语言库很精简, 是目前为止我见过最轻量级的语言, 这也是它快的原因之一. 数据结构单一, lua中的数据结构只有一个table, 跟js一样属于基于原型的继承形式, 很灵活. 要说缺点, 其实也挺纠结的, 它的工具库非常少, 不太适合高速开发, 很多工具库你都找不到, 还要自己手写, 当初用来和mysql交互的时候用的orm都是我们自己手写的, 实在是找不到像样的. 不是说一定要多丰富, 只是重复造轮子是很影响开发效率的.

最后说说javascript, 这半年我一直围着它玩了, 工作需要嘛. node.js的优秀是显而易见的, 单线程异步基于时间出发的设计让它很适合高并发的场景, 有人说node.js只适合IO密集型的应用, 碰到计算密集型就是软肋了. 我个人觉得, 没有什么软肋不软勒的, 重要的是使用的人, 是否用了最恰当的方式处理, 代码的优雅与否不也正体现在此么. 天然的支持websocket是node.js的一大优势, 而且不光是支持, 还支持的很好, 因为node.js本身的api简洁和易用性, 让使用者觉得使用起来感觉很容易, 毫无违和感的创建连接传输数据, 又是事件触发回调, 处处体现了高实时特点. 最最最牛逼的是node.js的引擎—v8, 性能不用说了, 之前一段时间看了几篇对v8的研究博客, 让我对v8对javascript的优化方式有了更深一层的理解. 回头会把链接追加到这篇blog里跟大家分享. 年末的时候读了一本很好的node.Js的书, 朴灵大大的<<深入浅出NODEJS>>, 还是作者签名版, 如今我已经在读第三遍了. 对于这本书我毫不吝惜的给予最高的评价, 朴大大的文笔真好, 内容丰富实用, 很适合变实战边看, 最重要是他本人对node.js在各个场景中的表现非常的了解, 经验丰富, 很多细节研究的特别透, 但是讲出来却让你非常的明了, 真正的深入浅出, 总之, 是居家旅行, 杀人灭口的必备良书. 最后要说的是npm, 最好的包管理器, 没有之一, 结束.

最后

不知不觉间跌跌撞撞走了这么远. 回头看看, 足够充实, 向前眺望, 路途依然漫长, 好在做我想做, 想我所想, 爱我所爱.

新的一年, 加油!

Mongoose 同时建立多个数据库连接

| Comments

此篇用于备忘, 最近要拆分数据库, 但是保留用户部分共享, 因此会用到一个项目的代码连接多个数据库的情况, 以前用的是

   mongoose.connect()

用这个建立的数据库连接, 只能连接一个数据库, 要想同时连接多个, 需要用下面这个:

   mongoose.createConnection()

就现在项目的逻辑来说, demo大致如下:

   //首先创建两个数据库连接
   var mongoose = require('mongoose');
   var config = require('config');
   var conn1 = mongoose.createConnection(config.db1);
   var conn2 = mongoose.createConnection(config.db2);

创建模型测试:

  //创建两个用户模型
  var Schema = mongoose.schema;        
  var UserSchema1 = new Schema({
      nickname: String,
      age: Number,
      email: String,
  });

  var UserSchema2 = new Schema({
      nickname: String,
      age: Number,
      email: String,
  });

  //再来, 我们创建两个不同连接的module,
  var User1 = conn1.model('User', UserSchema1);
  var User2 = conn2.model('User', UserSchema2);

  //接下来, 写两个测试函数看看结果: 
  function save1() {
      new User1({
          nickname: 'one',
          age: 8,
          email: 'qiaoker@hello.com',
      }).save(function (err, user) {
          if (err) {
              console.error(err.toString());
          } else {
              console.log('===== user1 ========');
              console.log(user);
          }
      });
  }
  function save2() {
      new User2({
          nickname: 'two',
          age: 3,

      }).save(function (err, user) {
          if (err) {
              console.error(err.toString());
          } else {
              console.log('===== user2 ========');
              console.log(user);
          }
      });
  }

经测试, 两个函数分别生成了各自的数据库和集合, 实现了业务需要, 即使用指定数据库中的模型, 唯一要注意的是在使用不同的数据库的模型时, 要知道自己在用哪个连接, 连接对象的名字要起得清晰.

最后补充一个最近总要用到的array类型字段的筛选方法: elemMatch方法, 这个方法mongoose官方文档写的很省略.. 基本上没明白是干嘛用的, 无奈我去看了mongodb的对应方法, 才知道原来是筛选符合条件的array字段的doc用的, 大概的用法如下

   Model.where('friends').elemMatch({sb: '2b'}).execFind(callback);
   // 上面的查询语句可以筛选出friends这个数组字段中拥有{2b: '2b'}这个元素的所有的doc. 

Javascript中的参数传递问题

| Comments

这是一个非常容易混搅的经典问题, 关于js中的参数传递, 到底是按值还是按引用呢? 最近在工作中确实的遇到了这样的问题, 所以就写篇blog拿出来818好了, 当做复习了.

关于这个问题, 初学者很容易踏入这样一个误区, 那就是引用类型是按引用传递, 普通类型按值传递:

注意: 这是不正确的, 根据javascript高级程序第三版中的严格定义, 可以知道: javascript中的所有参数传递都是按值传递的.

的确, 从行为上来看, 好像真的是这样, 下面来看看书中的例子:

    function addTen(num) {
        num += 10;
        return num;
    }

    var count = 20;
    var result = addTen(count);
    console.log(count);    //20, 木有变化
    console.log(result);    //30, 它变态了:P

    上面这个程序很好理解, 普通类型嘛, 我们再来看看普遍认为的按引用传递的情况:

    function setName(obj) {
        obj.name = "jaycee";
    }

    var person = new Object();
    setName(person);
    console.log(person.name);     //"jaycee", 也没什么不对的;

    下面是重点了, 理解下面这个段代码是很重要的, 它说明了js中参数传递的本质是按值的:

    function setName(obj) {
        obj.name = "jaycee";
        obj = new Object();
        obj.name = "hippo";
    }

    var person = new Object();
    setName(person);
    console.log(person.name);    //"jaycee", 出现了一些疑惑, 也是最需要好好思考的地方

从上面的最后那段代码我们可以看到, 在setName()方法中, 我们在对传入的obj赋值后, new了一个新的对象, 并赋给了obj, 如果person是按引用传递的, 那么person就回自动被修改为指向其name属性值为hippo的新对象. 但是当我们访问person.name时, 却任然得到了name属性值为jaycee的结果, 说明即使在函数内部修改了参数的值, 但原始的引用仍保持不变. 当函数在内部重写obj时, 这个变量引用的就是一个局部的对象了. 而这个对象会在函数执行完毕后立即被销毁.