5年博客小结以及立 flag

这几年写博客的时间越来越少了。早期的时候写博客主要是因为想要做学习笔记,比如学到了xxx 或者用了 xx 做到了 yy 之类的,写的很开心。

之后写博客越来越少,除了因为工作忙加班加成狗之外。还有明显的问题是,发觉博客越来越难写了。而觉得越来越难写的原因,则是因为自己懂得越来越多了。

知道的越多,写出来的越少,看起来很矛盾,其实这是博主这几年成长中碰到的一个非常真实的问题。

说起来有点好笑就好像,小学的时候上自然课,听说牛顿和记者的故事一样。画了两个圈,一个大圈一个小圈,分别表示两人的知识量。然后圈外则是不知道的知识一样。

现在看来,这个故事是非常真实的。因为知道的越多,就越会发现自己的无知。

写博客去记录或者解释问题的时候,深深感受到的日益增长的困难。因为懂的多了,所以在一些问题的细节上会察觉到更多的问题,从而导致原本的一个问题不敢去写,或者是写了一半不想含糊带过于是就此搁笔。

要量化这个问题来看,好比一个问题,你要了解 90分了,才能写出来一个50分的文章。如果只了解 60分 那么基本写不出来什么东西,还不如直接转载别人的教程。

前不久有幸碰到陈皓(左耳朵耗子),并询问了几个目前比较在意的问题(非技术)。谈话过程其实并没有聊到这个问题。不过陈老师确实给了我很深的印象。

当时的问题记得是问,关于平常注意力分散的情况,比如写一个代码,查一个 API,最后问题发散出去看了很多其他与手头工作无关的技术资料。陈老师但是的说法挺简单,就是“极端一点”感兴趣就继续深究,继续学下去不要回头。

另一个问题真是关于技术的选择上,目前的技术非常快,博客、IT咨询很多,也出现了例如掘进、伯乐在线之类专门整理咨询的网站。不过陈皓老师的想法依旧极端,不需要特意去理会这些咨询,大概是做好自己就行了。

所以现在想想,还是准备重新开始梳理下自己的想法和知识。碰到细节问题也都多列举,尽量试试“求甚解”。所以也准备立个 flag,今年剩下的时间每周都写几篇博文。

ERR_INCOMPLETE_CHUNKED_ENCODING koa node

请求突然出现了 ERR_INCOMPLETE_CHUNKED_ENCODING 这个报错,貌似 http 返回的 chunk 跪了。虽然已经修复了,但还是没搞清楚发生了什么。

谷歌娘说在 nginx 的 location 里面加上 proxy_buffering off; 然后重启 nginx 果然好了。

问题是,我随后注释掉这个依旧能没问题,所以是 nginx 闹脾气了需要重启?

5628dd6ecd9fa100f371_size30_w521_h534

Cannot read property pm2_env of undefined

[PM2][WARN] Applications x-filter not running, starting...
/usr/local/lib/node_modules/pm2/lib/CLI.js:461
        Common.printOut(cst.PREFIX_MSG + 'App [%s] launched (%d instances)', data[0].pm2_env.name, data.length);
                                                                                    ^

TypeError: Cannot read property 'pm2_env' of undefined
    at /usr/local/lib/node_modules/pm2/lib/CLI.js:461:85
    at /usr/local/lib/node_modules/pm2/node_modules/pm2-axon-rpc/lib/client.js:45:10
    at Parser.<anonymous> (/usr/local/lib/node_modules/pm2/node_modules/pm2-axon/lib/sockets/req.js:67:8)
    at emitOne (events.js:96:13)
    at Parser.emit (events.js:188:7)
    at Parser._write (/usr/local/lib/node_modules/pm2/node_modules/amp/lib/stream.js:91:16)
    at doWrite (_stream_writable.js:307:12)
    at writeOrBuffer (_stream_writable.js:293:5)
    at Parser.Writable.write (_stream_writable.js:220:11)
    at Socket.ondata (_stream_readable.js:555:20)

碰到这个心情不好,最后发现。其实只要跑一下 pm2 update 就好了。大致是因为更新了 pm2 没有跑这个命令出来的问题。

mongodb 通过数组某一元素查询

> db.test.insert({ arr: [ 1, 2, 3, 4, 5] })
WriteResult({ "nInserted" : 1 })

> db.test.insert({ arr: [ 5, 6, 7, 8, 9 ] })
WriteResult({ "nInserted" : 1 })

> db.test.find({ arr : { $in : [ 3 ] } })
{ "_id" : ObjectId("5779d5b99f113cca8e7002c1"), "arr" : [ 1, 2, 3, 4, 5 ] }

> db.test.find({ arr : { $in : [ 5 ] } })
{ "_id" : ObjectId("5779d5b99f113cca8e7002c1"), "arr" : [ 1, 2, 3, 4, 5 ] }
{ "_id" : ObjectId("5779d6879f113cca8e7002c2"), "arr" : [ 5, 6, 7, 8, 9 ] }

> db.test.find({ arr : { $in : [ 3, 4, 5 ] } })
{ "_id" : ObjectId("5779d5b99f113cca8e7002c1"), "arr" : [ 1, 2, 3, 4, 5 ] }
{ "_id" : ObjectId("5779d6879f113cca8e7002c2"), "arr" : [ 5, 6, 7, 8, 9 ] }

> db.test.find({ arr: 5 })
{ "_id" : ObjectId("5779d5b99f113cca8e7002c1"), "arr" : [ 1, 2, 3, 4, 5 ] }
{ "_id" : ObjectId("5779d6879f113cca8e7002c2"), "arr" : [ 5, 6, 7, 8, 9 ] }

kibana + elasticsearch 使用 Chrome/safari 请求 403 forbidden

elasticsearch 版本 2.3.1

由于 Elasticsearch 在处理 CORS 的一个BUG,kibana 在 firefox 上是正常,使用 chrome / safari 则会报错

  1. Normal user-agent is ok
    curl -v 'https://xxx.my.host/elasticsearch/_mget?timeout=0&ignore_unavailable=true&preference=1465897339430' -H 'origin: https://xxx.my.host' -H 'user-agent: Rajax/1 Redmi_3/ido Android/5.1.1 Display/LMY47V Eleme/5.10.2 ID/fa0ef395-659a-3150-b347-f258248013af; KERNEL_VERSION:3.10.49-perf-g6241083 API_Level:22 Mozilla/5.0 (Linux; Android 5.1.1; Redmi 3 Build/LMY47V; wv)' -d '{"docs":[{"_index":".kibana","_type":"config","_id":"4.5.1"}]}'
    *   Trying 115.x.x.42...
    * Connected to xxx.my.host (115.x.x.42) port 443 (#0)
    * TLS 1.2 connection using TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
    * Server certificate: *.ele.me
    * Server certificate: GeoTrust SSL CA - G3
    * Server certificate: GeoTrust Global CA
    > POST /elasticsearch/_mget?timeout=0&ignore_unavailable=true&preference=1465897339430 HTTP/1.1
    > Host: xxx.my.host
    > Accept: */*
    > origin: https://xxx.my.host
    > user-agent: Rajax/1 Redmi_3/ido Android/5.1.1 Display/LMY47V Eleme/5.10.2 ID/fa0ef395-659a-3150-b347-f258248013af; KERNEL_VERSION:3.10.49-perf-g6241083 API_Level:22 Mozilla/5.0 (Linux; Android 5.1.1; Redmi 3 Build/LMY47V; wv)
    > Content-Length: 62
    > Content-Type: application/x-www-form-urlencoded
    >
    * upload completely sent off: 62 out of 62 bytes
    < HTTP/1.1 200 OK
    < Server: nginx/1.9.6
    < Date: Wed, 15 Jun 2016 03:47:01 GMT
    < Content-Type: application/json; charset=UTF-8
    < Content-Length: 116
    < Connection: keep-alive
    < Vary: Accept-Encoding
    <
    * Connection #0 to host xxx.my.host left intact
    {"docs":[{"_index":".kibana","_type":"config","_id":"4.5.1","_version":1,"found":true,"_source":{"buildNum":9892}}]}
    

  2. Got 403 forbidden with Chrome/Safari user-agent…

    curl -v 'https://xxx.my.host/elasticsearch/_mget?timeout=0&ignore_unavailable=true&preference=1465897339430' -H 'origin: https://xxx.my.host' -H 'user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_5)' -d '{"docs":[{"_index":".kibana","_type":"config","_id":"4.5.1"}]}'
    *   Trying 115.x.x.12...
    * Connected to xxx.my.host (115.x.x.12) port 443 (#0)
    * TLS 1.2 connection using TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
    * Server certificate: *.my.host
    * Server certificate: GeoTrust SSL CA - G3
    * Server certificate: GeoTrust Global CA
    > POST /elasticsearch/_mget?timeout=0&ignore_unavailable=true&preference=1465897339430 HTTP/1.1
    > Host: xxx.my.host
    > Accept: */*
    > origin: https://xxx.my.host
    > user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_5)
    > Content-Length: 62
    > Content-Type: application/x-www-form-urlencoded
    >
    * upload completely sent off: 62 out of 62 bytes
    < HTTP/1.1 403 Forbidden
    < Server: nginx/1.9.6
    < Date: Wed, 15 Jun 2016 03:46:08 GMT
    < Transfer-Encoding: chunked
    < Connection: keep-alive
    <
    * Connection #0 to host xxx.my.host left intact
    

将 Elasticsearch 升级到 2.3.3 可以解决。博主在论坛中讨论的原帖:https://discuss.elastic.co/t/got-403-forbidden-with-chrome-user-agent/52812

Mongodb Error: network error while attempting to run command ‘isMaster’ on host

$ mongo
MongoDB shell version: 3.2.4
connecting to: test
2016-05-16T14:33:58.461+0800 E QUERY    [thread1] Error: network error while attempting to run command 'isMaster' on host '127.0.0.1:27017'  :
connect@src/mongo/shell/mongo.js:224:14
@(connect):1:6

exception: connect failed

搜了一下有说一个解决方案是在启动参数上加个flag

Solution: add the following option to your mongod command
--bind_ip 127.0.0.1 

不过燃冰暖,随后检查了下日志发现其实就是连接数过多,

...
2016-05-16T14:33:28.311+0800 I NETWORK  [initandlisten] connection accepted from 127.0.0.1:50075 #200 (200 connections now open)
2016-05-16T14:33:28.311+0800 I NETWORK  [initandlisten] connection accepted from 127.0.0.1:50074 #201 (201 connections now open)
2016-05-16T14:33:28.311+0800 I NETWORK  [initandlisten] connection accepted from 127.0.0.1:50089 #202 (202 connections now open)
2016-05-16T14:33:28.312+0800 I NETWORK  [initandlisten] connection accepted from 127.0.0.1:50088 #203 (203 connections now open)
2016-05-16T14:33:28.312+0800 I NETWORK  [initandlisten] connection accepted from 127.0.0.1:50087 #204 (204 connections now open)
2016-05-16T14:33:38.360+0800 I NETWORK  [initandlisten] connection accepted from 127.0.0.1:50092 #205 (205 connections now open)
2016-05-16T14:33:38.360+0800 I NETWORK  [initandlisten] connection refused because too many open connections: 204

根据日志里面的 accepted from ip:port ,找到端口,然后 lsof -i tcp:50075 之类的命令找到访问太多的进程,问题基本上就找到了。

实际上的问题应该是 “connection refused because too many open connections: 204”

node.js 调用 NATS 与 disque 性能简单对比

比较新的消息队列 server, NATSdisque 的对比。

仅插入

disque

Amount并发 100 500 1000 2000 4000
10w 3568.814ms 1910.550ms 2322.112ms 2577.898ms 2780.189ms
100w 32951.437ms 19610.882ms 20488.052ms 21875.545ms 26354.094ms

NATS

Amount并发 100 500 1000 2000 4000
10w 4486.236ms 4162.363ms 4330.767ms 4458.188ms 4987.342ms
100w 40481.896ms 41818.800ms 41704.926ms 45198.646ms 46906.605ms

双方的插入都在 500 的并发时表现还行,所以后面的测试用这个并发量来插入。

同时插入并读取

测试情况:

插入节点 : Server 节点: 读取节点 = 1: 1: N

disque

10w 级

2327.073ms 11085.363ms
3970.004ms 8064.683ms/8097.301ms
7637.522ms 8798.931ms/8804.586ms/8820.094ms/8986.903ms

100w 级

33974.781ms 128753.262ms
44862.931ms 90111.012ms/90975.795ms
67741.766ms 75597.516ms/75983.190ms/76229.671ms/76412.002ms

NATS

10w 级

6292.183ms 6268.520ms
6172.031ms 3673.333ms/3673.315ms
6127.849ms 2332.848ms/2332.729ms/2338.836ms/2339.670ms

100w 级

55505.581ms 55473.022ms
54234.438ms 29926.494ms/29926.681ms
52572.856ms 16027.231ms/16027.502ms/16025.114ms/16015.165ms

根据数据可以简单看出来, C语言编写的 disque 在纯插入的时候速度确实会快一些。然而到了同时插入并读取的时候, disque 的读取速度就慢了很多,可以看出来 1对多个客户端让 disque 在调度上产生了不小的消耗,而且读取的速度确实也比较慢。

NATS 就比较让人惊喜了,大概是因为异步式的架构,让 NATS 可以不关心插入的确认而是更注重调度和转发,所以让 NATS 的队列读取速度非常快,在跑测试的可以明显的感觉到,在 1:1:4 的时候 NATS 的流程分为 ①插入过程 ②clients读取 ③确认,NATS 的这种架构方式让流程②变得非常迅速。

使用这两个消息队列虽然不会内存泄漏,不过 disque 上限是 1779155,按照博主本机的测试数据看插入的快读取的慢随着时间推移总是有塞满的风险。disque 这种情况大概是因为设计的是安全队列的原因,安全队列类似 Linux 信号中的后 32 个用户自定义信号,不会因为当时没收到就丢失。而 NATS 的队列则属于不安全的队列,如果当时没收到也不会保留该消息,所以 NATS 比较起来塞满的风险更低。

测试环境:

macbook pro
CPU: 2.7 GHz Intel Core i5
MEM: 8 GB 1867 MHz DDR3

测试代码:

node-gnats/req.js
node-gnats/get.js
thunk-disque/req.js
thunk-disque/get.js

PS:
1) 博主这里只测了 1:1:N,没有测 N:1:M 和 N:L:M。
2) 一些比较高级的特性没有开启测试。
如果这些没关注到的这些地方有反转、文中有问题或者有更好的客户端推荐还请评论告知(或者电邮博主 lellansin@gmail.com)。