Monthly Review 2014-10
工作:
- 本月的 git commits 还不足上个月的一半。
- Redmine 上关掉 42 issues,当然有部分是无法复现或拒绝需求。
- 完成拖延一年的运营管理界面,这部分一周可能都用不了一次,一直没动力去做。
- 捡起 Docker 想做一下 CI,发现最大的问题不是环境,而是没有写测试用例的习惯。
- 除了没有测试,大部分项目都没有文档,或者更新不及时,接下来一个月先把自己手上的项目文档补全。
- 暴漏出来的问题就是自己的开发模式还处于比较原始的小作坊形式。VCS+Code Review 全靠自觉+代码强迫症,测试和部署上线全手工操作,没有流程,而这却是最重要的。
生活:
- 想六六。
- 考虑把娃带北京自己带,然后就没有然后了,哎。
- 如果独生子女有什么好处的话,孩子在需要帮忙的时候父母没有太多其他牵挂算一个。
- 晚上买菜做饭,对自己的厨艺还算满意,就是刀工太差。
Jekyll in Docker
最近又捡起 Docker,打算用在团队内做一些 CI 工作。拿 Jekyll 练手,记一下笔记:
FROM ruby:2.1.3
MAINTAINER Heyward Fann <[email protected]>
RUN gem install github-pages
RUN apt-get update && apt-get install -y curl && rm -rf /var/lib/apt/lists/*
ENV NODE_VERSION 0.10.33
RUN curl -SLO "http://nodejs.org/dist/v$NODE_VERSION/node-v$NODE_VERSION-linux-x64.tar.gz" \
&& tar -xzf "node-v$NODE_VERSION-linux-x64.tar.gz" -C /usr/local --strip-components=1 \
&& rm "node-v$NODE_VERSION-linux-x64.tar.gz"
WORKDIR /blog
EXPOSE 4000
ENTRYPOINT ["jekyll"]
CMD ["serve"]
Then:
docker build --rm=true --tag="blog:0.0.2" .
docker run --rm -p 4000:4000 -v /ABSOLUTE/PATH:/blog blog:0.0.2
boot2docker ip
thenhttp://IP:4000
- OR
docker run --rm -v /ABSOLUTE/PATH:/blog blog:0.0.2 build
笔记:
- Base image 建议选用
debian:wheezy
,如果需要编译环境可用buildpack-deps:wheezy|jessie
,相对 Ubuntu image 要小很多。 - 尽量不安装编译环境,直接包管理工具或二进制文件,注意清理缓存文件。
- 尽量少的
RUN
命令,减少 layers 数量,尽可能在一个 RUN 组合完成多个操作,比如 ruby。 - 配合
.dockerignore
忽略不需要的文件。 - build 或 run 的时候加上
--rm=true
自动删除中间容器。 CMD
和ENTRYPOINT
都是docker run
的入口,只是在参数处理上不同。CMD 可以被 run 后面的命令替换,而 ENTRYPOINT 是把 run 后面的作为参数传入。- CMD 配合 ENTRYPOINT 一起用很不错,如果没有参数,执行的就是 ENTRYPOINT+CMD 组合起来的功能,加上参数就会把 CMD 替换掉,执行另外的命令。
- OS X 下用
boot2docker
要注意 IP 不是本机或 127.0.0.1,而是boot2docker ip
. - 如果是 Golang 二进制程序,完全可以构建一个空 image 执行,比如 Building Docker Images for Static Go Binaries.
参考:
- Dockerfile Best Practices
- Dockerfile Best Practices - take 2
- Building good docker images
- 15 分钟掌握 15 个 Docker 小窍门
就目前的情况,用 Docker 构建测试环境很方便,麻烦的是测试用例和测试脚本整理,小团队基本以业务为主,很少有时间或没有意识的去写测试用例,只是在完成具体业务后针对该功能进行测试,没法系统的进行测试,这个需要在后面工作中重视起来。
Zsh Shared History
You can share every commands between all terminals with Zsh shared history. In your .zshrc
:
# enable shared history
setopt inc_append_history
setopt share_history
# disable shared history
unsetopt inc_append_history
unsetopt share_history
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 年的,人特老实,不像其他中介满嘴跑火车。在确定租房意向后,房东很直接的提议私单,就是我和房东直接签,然后出一半中介费给哥们作为辛苦费,这样他能拿到比提成多一倍的钱,我也能省一半中介费。我当然是愿意,不过哥们支吾半天说不行,说知道这样是来钱快,但是他不想这么干,圈子里这么做的人很多,但他不喜欢,要按原则来。当时房东还说我俩可以把他踢开直接签,这样连一半中介费我都不用出。中介哥们当时很憋屈,感觉眼圈都是红的,又很无力。房东离开后我俩单聊,他说理解,只是自己不喜欢。然后,有那么一瞬间,我决定按照正规流程来办,当然我要出全部中介费。签合同的时候才发现,这是他入行一个多月的第一单。能看出他很紧张,写字手都在抖,我不知道这第一单对他有多深的意义,也许若干年后他回想起来依然会激动?或者骂自己傻逼为什么没有直接拿钱?或者笑话有个傻逼居然答应出全部中介费?我只记得当时那一瞬间我的想法:
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 文档。记几点笔记:
- 用 OpenResty(ngx_lua) 作为前端 Nginx 代理和缓存服务器,好处是可以用
set_by_lua
计算赋值变量,原生 set 语法不够灵活。 proxy_cache_path
指定缓存文件目录,和proxy_temp_path
最好设置在同一文件分区下,缓存内容是先写在 temp_path,然后移动到 cache_path,不同文件分区会影响性能。keys_zone
命名并设置缓存的内存空间大小,要注意的是这个内存空间并不保存缓存文件,而是缓存文件的元信息(meta information),所以不必太大,根据文档 1M 大小可保存 8000 文件的元信息,可以根据缓存文件数量进行设置。inactive=30m
表示 30 分钟没有被访问的文件会被 cache manager 删除,max_size=500m
表示缓存目录最大限制 500M 磁盘空间。proxy_cache
指明用哪个缓存空间,proxy_cache_valid
是缓存的有效时间,可以针对不同响应状态设置不同的有效时间,比如proxy_cache_valid 404 1m;
,默认只对 200/301/302 响应进行缓存。- 缓存文件数量过多会影响 proxy_cache 性能,Nginx 在启动时 cache manager 会检查并读取缓存文件的元信息到内存,这个读取是有限制的,默认情况下 cache manager 每次读取 100 个文件的元信息,每次读取限时 200ms,间隔 50ms 进行下次读取。
- 缓存文件并不是越多越好,所以 cache_key 的设计非常关键。代理或 URL 跳转常常会添加的无用请求参数,这就会出现不同的 cache_key 保存了多份相同的缓存内容,这对缓存效果影响很大。通过 ngx_lua 可以对 URL 参数进行过滤,保证 cache_key 唯一。
table.sort(args)
对 URL 参数重排序,避免/api?page=1&count=10
/api?count=10&page=1
生成两份缓存的情况。$upstream_cache_status
可以获取缓存状态,包括HIT/BYPASS/MISS/EXPIRED
,可以记录到 access_log 和 response header,用以计算缓存命中率。proxy_no_cache
如果有值且不为 ‘0’,该请求的 response 就不会生成缓存。proxy_cache_bypass
如有有值且不为 ‘0’,该请求会忽略缓存。- proxy_cache 不支持手动清除缓存,可以通过第三方模块 ngx_cache_purge 来清除指定 URL 的缓存。
proxy_cache 非常的简单高效,合理使用可以有效的减轻后端服务压力,提升服务访问速度。