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 非常的简单高效,合理使用可以有效的减轻后端服务压力,提升服务访问速度。
Octopress to Jekyll
周末花时间把 blog 从 Octopress 2 迁移到 Jekyll。
Octopress 是个非常好的 Jekyll-blog 解决方案,尤其是在 Jekyll 0.x 时代,Octopress 有不错的模版,丰富的扩展功能,缺点就是麻烦,需要在本地生成页面。
Jekyll 在过去一年开发迭代非常快,大量的新功能新特性加入让 Octopress 显得不那么必要。GitHub Pages 最近也升级支持 Jekyll 2.2,于是就有了这次迁移。
- 通读 Jekyll 文档,了解新功能特性。
- 本地安装
gem install github-pages
,模拟 GitHub Pages 环境测试。 - 修改
permalink: /blog/:year/:month/:day/:title
保持链接不变。 - 设置 markdown 解析器为
markdown: redcarpet
,支持 GFM。 - 分页设置
paginate: 10 paginate_path: "blog/page/:num"
,保持兼容。 - 配置 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
,但功能很弱,所以有很多第三方的辅助开发应用:
这几个是独立的应用,配套相应的 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.
CocoaPods Podspec for Static Library
新建 podspec 可以用命令 pod spec create YourLibrary
自动生成 YourLibrary.podspec,然后根据具体项目进行修改。对于 libYourLibrary.a 形式的静态库需要注意的地方:
s.source_files = '*.h'
s.preserve_paths = 'libYourLibrary.a'
s.library = 'YourLibrary'
s.xcconfig = { 'LIBRARY_SEARCH_PATHS' => '$(PODS_ROOT)/YourLibrary' }
preserve_paths
可以用 *.a
模糊匹配或指明静态库名,LIBRARY_SEARCH_PATHS
指定路径。
Reload HAProxy on the fly
haproxy -f /etc/haproxy/haproxy.cfg -p /var/run/haproxy.pid -sf $(cat /var/run/haproxy.pid)
Performance on Optimization
Donald Knuth:
Premature optimization is the root of all evil.
高性能的程序是一个程序员应该有的追求,但是过早的性能优化往往起到反作用,浪费时间,拖慢进度等等。如何尽量少的优化投入同时达到高性能?
- 一个高性能框架,赢在起跑线,比如
ngx_lua
。 - 一套成熟高效的技术架构解决方案,比如 Tornado + PostgreSQL + Redis。
- 一开始就按照最佳实践写代码,把常规需要优化的地方降到最少。
- 不要只局限于软件层,硬件升级往往比软件优化更给力,比如 SSD。
Vim 文本选择范围
Vim 文本选择时可以用 a
i
指定选择范围。a
代表一个整体(block),i
代表 inner。比如:
vaw
包括单词和单词后的空格,viw
只选中单词。
vat
- select a tag block, 包括 <tag></tag>
本身,vit
- select inner tag,只选择 <tag></tag>
包起来的部分。
vab
选中包括 ()
在内的文本,vib
不包括 ()
自身,等同 va(
va)
, vi(
vi)
.
vaB
选中包括 {}
在内的文本,viB
不包括 {}
本身,等同 va{
va}
, vi{
vi}
, 类似有 va[
vi[
。
vip|vis
选中一段落文字,vip = visual inner paragraph. vis = inner sentence.
将 v
换为 d
是就变成了删除操作,删除范围同上。
查看帮助 :help v_<whatever>
.