Two Hard Things

There are only two hard things in Computer Science: cache invalidation and naming things. – Phil Karlton

Monthly Review 2014-09

选择

月初一个项目计划是客户端+服务端都由我负责,项目进行中我把客户端交了出去,保证进度是一个原因,毕竟是完全新的业务功能,服务端要做东西还挺多,主要原因是面对客户端开发,忽然手生,表现在这个东西我知道,着手代码的时候要愣一愣。

算下来有一年多没有 系统 的写 iOS,正好 iOS 7 一代。这一年 iDev 相关的学习一直没落下,但缺乏实际项目锻炼,解决问题的思路还有,具体到某一个技术点,比如写一个毛玻璃模糊效果,就要愣一下,需要查文档了。

出现这种情况也在意料之中,虽说要做全栈开发,终究是要有些侧重,目前还是以服务端为主。服务端开发现在主要是业务驱动,技术长进有限,最近在看 nsq,争取在业务和技术上都能有进步。客户端方面还是技术关注+学习,具体开发上给我一点时间还是有信心回到之前的熟练水平 :D


最后还是选择把六六留在老家。走这一步我和老婆俩人没少哭,就现阶段条件,执意带六六到北京的结果可能更糟,北京的居住条件六六和妈是否适应,爸一个人在家的生活,妹妹也还没毕业,就算狠狠心带过来也不能长久,这些都让人头大。六六在家爸妈肯定能照顾好,就是想孩子受不了,尤其是大了越来越好玩,真心舍不得。每个月多往家跑跑吧。

六六健康成长。

租房小事

因为自己的犹豫,错过了一套条件非常不错的房子。房东同时把房子挂在链家和我爱我家,链家带我看房的时候我在想看看别的房子再说,等一天应该没问题,结果就被我爱我家出手。所以说:你越担心某种情况发生,那么它往往就是会发生,墨菲定律:

Anything that can possibly go wrong, does. via

这事对我来说是个教训,做事太瞻前顾后,不够果断。不过我想记的不是这件事,是下面这件,不知道我做的是对是错还是很傻逼的事。

带我看房的中介哥们,90 年的,人特老实,不像其他中介满嘴跑火车。在确定租房意向后,房东很直接的提议私单,就是我和房东直接签,然后出一半中介费给哥们作为辛苦费,这样他能拿到比提成多一倍的钱,我也能省一半中介费。我当然是愿意,不过哥们支吾半天说不行,说知道这样是来钱快,但是他不想这么干,圈子里这么做的人很多,但他不喜欢,要按原则来。当时房东还说我俩可以把他踢开直接签,这样连一半中介费我都不用出。中介哥们当时很憋屈,感觉眼圈都是红的,又很无力。房东离开后我俩单聊,他说理解,只是自己不喜欢。然后,有那么一瞬间,我决定按照正规流程来办,当然我要出全部中介费。签合同的时候才发现,这是他入行一个多月的第一单。能看出他很紧张,写字手都在抖,我不知道这第一单对他有多深的意义,也许若干年后他回想起来依然会激动?或者骂自己傻逼为什么没有直接拿钱?或者笑话有个傻逼居然答应出全部中介费?我只记得当时那一瞬间我的想法:

不要做让自己讨厌自己的事情。 1 2

Monthly Review 2014-08

工作上把一个去年就想过的设计码成并上线,目前状况良好,说明当初的设计思路是没有问题的。之所以拖了这么久是因为项目时间比较紧(是我比较懒),最主要的原因是一直没有下狠心去重构。这部分功能运行正常,可能在某些情况下会有性能问题,但绝大数情况下完全不用担心性能,而新的设计和之前的实现不同,作为一个线上服务首先要考虑的是服务的稳定性,其次是新设计的兼容性,所以一直拖到现在。现在回头看开发要有点魄力,有时候自己过于小心了。

服务端开发的架构设计很重要,前期要多做思考工作,不能上来就去代码,要考虑可能出现的功能需求,思考性能瓶颈,有了好的设计再去代码效率也会更好。

生活上给自己买了一份商业保险,30年寿险,是自己对家庭的责任。核保的时候因为 BMI 超标,每年保费多了 150,体重又一次打击了我,减肥正式开始,从 19 号开始每天晚上锻炼,根据 Nike+ Running 统计已经 66 公里,继续坚持。

由于工作原因老婆打算提前回北京,这几天抽空一直在看房子,打算整租一套,不再合租,不管六六来不来北京。以前老感觉整租花钱多,将就一下也就过了,不要太过于奢侈。现在看对我们自己太辛苦了,自己都过不好拿什么承担其他责任。老婆回去这两三个月,每天也不做饭,周末都在单间里窝着,就算出去转转也是一个人,没人说话,行尸走肉一般,再这样下去怀疑自己就要抑郁症了。

要好好的对待自己,好好生活,好好工作。

Nginx proxy_cache

Nginx proxy_cache 可以将后端动态请求的返回内容进行缓存,原理是 URL 作为 cache_key,将内容缓存到磁盘,新请求符合缓存规则的话直接读取缓存内容返回。

proxy_cache_path /tmp/ngx_cache/proxy_cache_dir levels=1:2 keys_zone=ngx_cache:10m inactive=30m max_size=500m;
proxy_temp_path  /tmp/ngx_cache/proxy_temp_dir;

server {
    proxy_cache ngx_cache;
    proxy_cache_valid 10m;
    add_header  Nginx-Cache "$upstream_cache_status";

    set $no_cache '';
    set_by_lua $cache_key "
        local no_cache = false
        if ngx.var.http_cookie and string.find(ngx.var.http_cookie, 'user') then
            # 带 cookie 的请求(比如登录用户)忽略缓存
            no_cache = true
        end

        if ngx.var.uri == '/api/test' then
        	  #某些 URL 的请求强制缓存,不管是否有 cookie
            no_cache = false
        end

        if no_cache then
            #确定忽略缓存就不再计算 cache_key
            ngx.var.no_cache = 'true'

            return ngx.var.uri
        end

        local uri_args = ngx.req.get_uri_args()
        local args = {}
        for k, v in pairs(uri_args) do
            if k and v and type(v) == 'string' then
                if k == 'count' or k == 'sort' or k == 'page' then
                    #过滤掉非法请求参数
                    args[#args+1] = k .. '=' .. v
                end
            end
        end

        if #args > 0 then
            table.sort(args)
            return ngx.var.uri .. '?' .. table.concat(args, '&')
        else
            return ngx.var.uri
        end
    ";
    
    proxy_cache_key $cache_key;
    proxy_no_cache $no_cache;
    proxy_cache_bypass $no_cache;
    
    location / {
	     proxy_pass http://localhost:8080;
    }
}

配置 proxy_cache 很简单,建议先通读 NGINX Content Caching 文档。记几点笔记:

  1. 用 OpenResty(ngx_lua) 作为前端 Nginx 代理和缓存服务器,好处是可以用 set_by_lua 计算赋值变量,原生 set 语法不够灵活。
  2. proxy_cache_path 指定缓存文件目录,和 proxy_temp_path 最好设置在同一文件分区下,缓存内容是先写在 temp_path,然后移动到 cache_path,不同文件分区会影响性能。
  3. keys_zone 命名并设置缓存的内存空间大小,要注意的是这个内存空间并不保存缓存文件,而是缓存文件的元信息(meta information),所以不必太大,根据文档 1M 大小可保存 8000 文件的元信息,可以根据缓存文件数量进行设置。
  4. inactive=30m 表示 30 分钟没有被访问的文件会被 cache manager 删除,max_size=500m 表示缓存目录最大限制 500M 磁盘空间。
  5. proxy_cache 指明用哪个缓存空间,proxy_cache_valid 是缓存的有效时间,可以针对不同响应状态设置不同的有效时间,比如 proxy_cache_valid 404 1m;,默认只对 200/301/302 响应进行缓存。
  6. 缓存文件数量过多会影响 proxy_cache 性能,Nginx 在启动时 cache manager 会检查并读取缓存文件的元信息到内存,这个读取是有限制的,默认情况下 cache manager 每次读取 100 个文件的元信息,每次读取限时 200ms,间隔 50ms 进行下次读取。
  7. 缓存文件并不是越多越好,所以 cache_key 的设计非常关键。代理或 URL 跳转常常会添加的无用请求参数,这就会出现不同的 cache_key 保存了多份相同的缓存内容,这对缓存效果影响很大。通过 ngx_lua 可以对 URL 参数进行过滤,保证 cache_key 唯一。
  8. table.sort(args) 对 URL 参数重排序,避免 /api?page=1&count=10 /api?count=10&page=1 生成两份缓存的情况。
  9. $upstream_cache_status 可以获取缓存状态,包括 HIT/BYPASS/MISS/EXPIRED,可以记录到 access_log 和 response header,用以计算缓存命中率。
  10. proxy_no_cache 如果有值且不为 ‘0’,该请求的 response 就不会生成缓存。
  11. proxy_cache_bypass 如有有值且不为 ‘0’,该请求会忽略缓存。
  12. proxy_cache 不支持手动清除缓存,可以通过第三方模块 ngx_cache_purge 来清除指定 URL 的缓存。

proxy_cache 非常的简单高效,合理使用可以有效的减轻后端服务压力,提升服务访问速度。

Octopress to Jekyll

周末花时间把 blog 从 Octopress 2 迁移到 Jekyll

Octopress 是个非常好的 Jekyll-blog 解决方案,尤其是在 Jekyll 0.x 时代,Octopress 有不错的模版,丰富的扩展功能,缺点就是麻烦,需要在本地生成页面。

Jekyll 在过去一年开发迭代非常快,大量的新功能新特性加入让 Octopress 显得不那么必要。GitHub Pages 最近也升级支持 Jekyll 2.2,于是就有了这次迁移。

  1. 通读 Jekyll 文档,了解新功能特性。
  2. 本地安装 gem install github-pages,模拟 GitHub Pages 环境测试。
  3. 修改 permalink: /blog/:year/:month/:day/:title 保持链接不变。
  4. 设置 markdown 解析器为 markdown: redcarpet,支持 GFM。
  5. 分页设置 paginate: 10 paginate_path: "blog/page/:num",保持兼容。
  6. 配置 Google Analytics,Webmaster Tools 等。

测试没问题推送到 GitHub 即可。

Octopress 3 也改变策略,不再那么复杂,只是对 Jekyll 操作进行二次封装,方便使用。目前来看封装的功能都不太需要,一个简单的 Rakefile 就够了。

Monthly Review 2014-07

从这个月开始会在月底做一次月总结,其实最主要的目的是强迫自己每个月写点什么。

写自己,写给自己,写作技能上力图为读者负责,写作态度上只求为自己负责。

via KDr2.

工作上除了常规业务开发,调研并上线了 nginx proxy_cache,效果很是不错,为后面的访问爆发做准备。

断断续续在线看完《Go Web 编程》,对 Golang 的有了相对全面的了解,相比 OpenResty,Go 在性能上差距不大,开发效率上要比 Lua 高一些(不过 Go 的语法真心没有 Lua 简单明了),可以在一些项目上试试手。

慎独,又是一个人的一个月,过于宅的日子对自己心态很不好,所以这两周末有意识的让自己出去走走,做一些调整。

心态不好一方面是一个人,一方面是家里,我们这一代人和父母一辈在育儿上确实有很多分歧,尽管目标都一致。得感谢小姨一直在中间劝我妈,现在问题已经化解很多。

看着兄弟们升职买房结婚安定,加上家里、小孩的因素,北漂的不安定很让人心烦,这也是自己心态不好的主要原因,买房已经开始考虑并提上日程,希望自己能处理好这些事情。

Vim Golang 开发环境: vim-go

安装 Golang 并设置 $GOPATH:

export GOPATH="$HOME"
export PATH="$PATH:$GOPATH/bin"

Golang 官方提供了 Vim 开发工具 $GOROOT/misc/vim,但功能很弱,所以有很多第三方的辅助开发应用:

  1. gocode 自动代码补全
  2. godef 函数定义跳转,快捷键 gd
  3. goimports 自动 import 包管理
  4. gotags 展示当前代码里函数列表,配合 tagbar 使用

这几个是独立的应用,配套相应的 Vim 插件,单独安装很是繁琐。而 vim-go 是一整套的 Golang Vim 开发配置,安装插件后通过 GoInstallBinaries 安装 gocode, godef, goimports, gotags, golint, oracle, errcheck 以及相应的 Vim 插件、配色、代码块,非常方便。

Plugin 'fatih/vim-go' 安装,默认代码补全引擎是 Ultisnips,修改为 neosnippet let g:go_snippet_engine = "neosnippet"

我的 vimrc.

2014 年中总结

工作

看似做了很多,但没有明确成绩。

回看上半年工作,第一感觉就是这样。

服务端目前主要负责移动社区程序的功能开发和维护。年初花了点时间进行服务器迁移和优化,这半年社区在稳定性、速度上有了一点改进,包括新功能开发上基本满足客户端需求,为社区用户扩张提供了技术支持和保障。但是过于后端的开发往往给人的感觉就是看不到成绩,很多东西也没法用数字量化,只有在服务出问题的时候才会显现一下,也许这就是后端开发最大的寂寞吧。

客户端没有具体的应用开发,唯一能拿出手的就是对 Background Fetch 简单做了技术探索然后封装 SDK 使用,积累了静态库经验后协助几个内部服务 SDK 封装,方便使用。对于 iOS 7 以来新加的大部分 API 依然是停留在理论学习,缺乏实际项目实践。偶尔帮忙解决一些问题,不足一提。

技术上在前端开发有一些进步,实践了 Grunt/Gulp 开发流程,JS 水平有提升,虽然还是很菜,顺带对 Node.js 有了更多的了解和实践,下一步可以在具体项目中实战一下。用 Docker 搭建内部 GitLab 并尝试推进 Git 开发流程规范,目前来看效果还可以,下一步打算引入持续集成(CI)实践。

产品能力提升有限,这个和自己的工作重心有很大关系,目前还是希望以技术为主,然后平时要多注意参加产品讨论进行学习。

生活

产检让我切身体验了医院的挂号排队,医疗资源的分配不均是主要原因。北京的医疗条件是好,但要我们在北京生产却很不现实,一个很大的原因就是房子,家里来人照顾住哪?继续单间合租肯定不行,换大房子开销就要翻倍,所以还是决定回老家生。

送老婆回家之前还是换了房子,来北京四年第一次搬家。住是北漂怎么都绕不过去的一个问题,一直说不考虑房子的情况下在北京其实挺好,现实是你怎么可能忽视掉这个每天要待十小时的地方?现在住自如,略高于市场均价,好处是服务还不错,没有无良中介的打扰。

六月二十四日六六出生,女孩,很好看,但是过程很辛苦,老婆很辛苦,妈妈是最伟大的。我第一次手术通知书签字,那两个小时真的很难熬,紧张,焦虑,不安,却又不能垮,因为你是家里的顶梁柱,你得撑起来。这时候不会想你的工作你的收入你的事业甚至你的理想,只希望她们能健康。

老婆,我爱你,希望我们的六六健康成长。

Nginx Gzip

gzip on;
gzip_types text/plain text/css text/javascript application/json application/x-javascript;

nginx.conf 配置 gzip on; 即可打开 gzip 压缩,需要注意的是默认情况下 nginx 只对 text/html 类型进行压缩,所以需要设置 gzip_types

参考文档 ngx_http_gzip_module.