util.promise

在nodejs裡,大部分的io相關function是async,async透過callback返回處理結果, 因此在流程處理上容易因為連續多個async造成callback hell, 有些方式可以減緩這樣的問題:例如

  • async套件(https://www.npmjs.com/package/async),
  • ES2015的promise
  • ES2017的async await

都是處理這類的問題(async await在nodejs 7.6+支援)。 而promise和async await又能夠很好的配合使用。透過async await能夠大幅地消除callback hell, 並且很接近synchronous call的編寫流程。

但大多數的nodejs io function都是callback style的,無法直接被async await利用。 在nodejs8中有提供一個util function: util.promisify 能夠將callback style function轉成promise

var util = require('util');
 var fs = require('fs');
 var fswriteFile = util.promisify(fs.writeFile);
 <strong>async </strong>function main(){
   try{
     <strong>await </strong>fswriteFile('test.txt', 'hello world'); //await promise
     console.log('write successfully'); //上面的promise resolve後才會走到這一行
   }catch(e){
     console.log(e);
   }
 }
 main();

util.promisify使用時對於大部分callback style function FUNC(arg1, arg2, arg3…, callback) 不需特別注意,但少數callback參數不是在最後一個,則可以透過指定util.promisify.custom 告訴util.promisify要使用特定的promise
事實上可參考 setTimeout, setImmediate

{ [Function: setTimeout] [Symbol(util.promisify.custom)]: [Function] }

以下給出一個範例

var util = require('util');
 function setTimeout1(ms){
   return new Promise(function(resolve, reject){
     setTimeout0(resolve, ms);
   });
 }
 function setTimeout0(callback, ms){
   setTimeout(callback, ms);
 }
 setTimeout0[util.promisify.custom] = setTimeout1;
 var setTimeoutP = util.promisify(setTimeout0);
 console.log(setTimeoutP == setTimeout1);

對於像dns.lookup在callback中有多個value回傳的,nodejs也定義了customPromisifyArgs,使得promise回傳會將多個值以object key value型式返回

Posted in nodejs | Leave a comment

turn on NTFS case sensitive on Win10

NTFS下的檔案名稱是case sensitive,但是如果兩個檔案的檔名差異只有大小寫則需要用底層的api呼叫來處理。但在win10可以透過把linux subsystem打開,在powershell下輸入

Enable-WindowsOptionalFeature -Online -FeatureName Microsoft-Windows-Subsystem-Linux

並且針對資料夾設定

C:\Windows\System32\fsutil.exe file SetCaseSensitiveInfo D:\xxx\yyyy

Posted in System Administration | Leave a comment

exec path on linux

在處理nodejs addon時 手動執行node-gyp rebuild,js require編譯出來的.node檔時一直報錯


這個錯誤代表編譯與執行的nodejs版本不合

檢查在linux上執行命令的路徑

which node-gyp
/usr/local/bin/node-gyp

執行whereis node-gyp 看是否有其他的node-gyp

直接執行 which 所回報的node-gyp路徑是正確的版本
/usr/local/bin/node-gyp rebuild (這個是對應執行環境的nodejs /usr/local/bin/node)

但事實上 直接執行node-gyp 執行的路徑卻是/usr/bin/node-gyp

$PATH所顯示的也是先找 /usr/local/bin

後來發現是bash的hash cache造成的
type node-gyp

只要執行hash -d node-gyp 即可清掉
重新執行 node-gyp rebuild 就會對應正確的路徑

其他如hash -r 可參考

Posted in System Administration | Leave a comment

rabbitmq recovery fail處理

rabbitmq啟動失敗時,可查看log (/var/log/rabbitmq)或是使用command line方式啟動rabbitmq-server

可以看出 /var/lib/rabbitmq/mnesia/$HOST 下的 recovery.dets 可能損毀
檢查一下 ls -l recovery.dets 發現檔案大小是0 代表可能當時開啟檔案後寫入失敗

這在當硬碟空間不足時可能發生,rabbitmq預設小於50MB時會停止服務(一般是每10秒檢查一次 disk alarm on時 等待consumer消耗掉messages,但也有可能是其他process影響到磁碟空間)

When free disk space drops below a configured limit (50MB by default), an alarm will be triggered and all producers will be blocked. The goal is to avoid filling up the entire disk which will lead all write operations on the node to fail and can lead to RabbitMQ termination

當磁碟空間不足時,可以一併檢查一些系統log,也能發現蛛絲馬跡,例如/var/log/auth.log, /var/log/kern.log 看那段時間點(可參考recovery.dets的檔案modified time) log的寫入狀況或是內容,如果在系統未關機的情況下log短缺很多,可以判斷當時log可能寫不進去

因檔案毀損無法啟動的情形,將/var/lib/rabbitmq/mnesia/rabbit@HOST下的檔案清乾淨即可重新正常啟動rabbitmq

Posted in System Administration | Leave a comment

nmap

nmap -sP 192.168.2.0/24


用來偵測開機的ip mac,適合當不知道guest vm的ip時,先查除vm的網卡mac,再找出對應的ip

Posted in System Administration | Leave a comment

同一個port同時支援http/https

在nodejs下 httpolyglot套件可以讓server在同一個port同時支援http/https

其原理是透過偵測第一個byte 的差異來決定這條連線要走http or https
TLS的一開頭 record header 第一個byte是0x16,在HTTP裡的第一個byte不會出現此數值。

參考:
https://tls.ulfheim.net/
https://github.com/mscdex/httpolyglot

Posted in nodejs | Leave a comment

jq

這邊主要是介紹一個不錯的command line util: jq

jq主要是command line JSON filter,適合用在bash script處理json,特別是要取得特定的field,和處理formatting

bash script一般處理文字常用sed, awk,但是遇到json,單純的字串處理很難應付複雜的結構。

參考: https://stedolan.github.io/jq/manual/

Posted in System Administration | Leave a comment

tmux notes

tmux是一個終端管理的工具,類似screen但功能更強,特別是多了pane以及畫面同步的機制。

<prefix>: default ctrl+b (註: screen為 ctrl+a)

create a new window: <prefix> + c
next: <prefix> + n
prev: <prefix> + p
detach: <prefix> + d
kill: <prefix> + x (<prefix> + k)
jump: <prefix> + number
list windows and choose: <prefix> + w (<prefix> + ")

其他
tmux ls
tmux a
tmux a -t <session-name>
tmux -V (screen -v)

config:
~/.tmux.conf

unbind C-b
set-option -g prefix C-a
bind-key C-a send-prefix

一進tmux就起始預設session

if [ -z “$TMUX” ]; then
tmux attach -t default || tmux new -s default
fi

在tmux內設訂$TMUX環境變數 所以可以用是否設定變數來決定是否attach or create default session

kill server: tmux kill-server
move between two panes: <prefix> + ;
cycle through panes: <prefix> + o
split pane vertically: <prefix> + %
zoom current pane:  <prefix> + z
在.tmux.conf 建議可設定 set-window-option -g mode-keys vi 在複製區域時可避免<ctrl>+<space>與輸入法衝突
vi mode-keys 在複製貼上時可用 <prefix> + [ 進入copy-mode
<space> 起始複製區域 
<enter>結束複製區域
<prefix> + ] 貼上
<PgUp> 螢幕上捲
<PgDn> 螢幕下捲
<arrow> cursor移動
另外是attach同一個session畫面會同步,可以拿來做教學用途或是pair programming

參考:
https://fedoramagazine.org/4-tips-better-tmux-sessions/
https://hackernoon.com/a-gentle-introduction-to-tmux-8d784c404340

Posted in System Administration | Leave a comment

Object.defineProperty

Object.defineProperty(obj, prop, descriptor)

descriptor分成data descriptor和access descriptor,兩者不能混用。
If a descriptor has neither of value, writable, get and set keys, it is treated as a data descriptor. If a descriptor has both value or writable and get or set keys, an exception is thrown.

這兩者差別在於 data descriptor描述值與是否可以更改值,access descriptor將值則是用getter/setter來處理
data descriptor:
value: default undefined
writable: true 可以變更值
access descriptor:
get: function(){ return v;} //沒設定的話value會是undefined
set: function(v){} //當obj.prop = v時會呼叫
其他可以設定的是
configurable: true 可刪掉
enumerable: true 可在for(var prop in obj) 或是Object.keys(obj)列出

以上的設定如果不是使用Object.defineProperty 預設 writable: true, enumerable: true, configurable: true
使用Object.defineProperty的預設是 { value: undefined, writable: false, enumerable: false, configurable: false }
By default, values added using Object.defineProperty() are immutable (writable預設false)
所以不論是data descriptor或是access descriptor沒有設定value or getter,取得值的話都是undefined

這個主要用在做更細的權限控制,特別是寫library時,以及將method隱藏在enumeration
另外透過get set hook,可以將物件對象和外部資源進行連結。

參考:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty

Posted in nodejs | Leave a comment

mongodb connect slow

在實際mongoose連線時,發現使用mongodb://localhost/db連線常常要好幾十秒才會建起來,而mongodb://127.0.0.1/db就馬上可以連上,追蹤了底層發現原因主要是mongodb-core 3.x他預設是使用tcp family 6的參數建連線,如果在系統內沒有正確設定localhost對應的ipv6 dns lookup
(/etc/hosts ::1 或是 external dns的ipv6 dnslookup)就會等到查詢timeout,這時候mongodb nodejs driver才改用family 4的參數連線

node_modules/mongodb-core/lib/connection/connection.js

而127.0.0.1連線時,也是會帶family 6去連,但是在nodejs net.createConnection會進行dns.lookup 而 dns.lookup會再檢查一次字串是否為ip以及解出對應的family

https://github.com/nodejs/node/blob/master/lib/dns.js

這類的問題比較麻煩的是在不同的機器上不一定能reproduce,有可能ipv6 localhost設定正確或是對外查詢dns可以正常查到,所以在追問題的時候仍需從經過的source code著手

Posted in nodejs | Leave a comment