2013年1月29日 星期二

pecl-solr

pecl-solr 是一個 php 的 extension, 用 curl & libxml2 處理從 solr 吐回來的資料,
資料的格式可能是 xml, json 或是 serialize 過的 php array string.


這幾天在看 freebsd 的 pr, 發現寫 pecl-solr 的這群人非常的天才,
即使他們全部都有 @php.net 的帳號.


首先, 用 curl 去 solr 拉 search result, 如果格式是 xml 的話,
再用 libxml2 處理 search result, 轉成 php array.
(以上都是在 extension 裡面呼叫, 所以都是用到 c 的 function)


但是 build 出來的 solr.so 並沒有 link 到 libcurl & libxml2.
往上追到 Makefile, 發現根本沒有 "-lcurl -lxml2" 等 keyword,

Makefile 是透過 configure 產出的,
那是不是 configure 的時候出了什麼問題?

也不是, config.log 怎樣看都是對的,
有抓到 curl & libxml2 的 library & header file, 判斷都是對的.

configure 是透過 phpize 讀取 config.m4 產生的.
那 config.m4 有沒有問題?

config.m4 在 curl 判斷上, 將 -lcurl 的定義放進了 CURL_SHARED_LIBADD

最後會用 PHP_SUBST 來把要 link 的 library 放進 Makefile 裡面,
但是他的 config.m4 裡面是這樣的.

PHP_SUBST(SOAP_SHARED_LIBADD)

你明明是 solr extension, 為什麼要用 SOAP 這名字?
並且為什麼上面寫的是 CURL_SHARED_LIBADD,

第一, 名字寫錯了, 第二, 就算寫錯, 兩個寫的一樣也是兜的起來,

這樣怎麼可能會去 link 到對的 library?


然後又發現 pecl-solr 有一些 unit test, 跑了一下,
錯誤訊息裡面說我沒有裝 php curl extension.

code 怎樣看都是直接 call libcurl 的 c function, 為什麼要我裝 php curl extension?

再繼續看 php_solr.c 發現他在裡面 require 了 php curl & libxml2 extension.


真相大白, 為什麼在 runtime 的時候都不會有問題?

因為很白痴的要求一定要裝 php curl & libxml2, 所以不會發生 undefined symbol 的問題,

但是這不是脫褲子放屁嗎?


php-curl 大家用得比較多, 但是 php-libxml2 在我所知道的人裡面, 很少人直接 call 這個,
pecl-solr 只是直接 call c function, 為什麼要 require php-libxml2 extension?

2013年1月9日 星期三

工程師想要什麼?

我們組最近有個需求, 想要監控 mysql 各個 table 的 row size,

直接讓某台 cron 機器, 開 mysql connection 到各台 mysql 讀 INFORMATION_SCHEMA 是最直覺的,

也有同事說, 讓 mysql server 自己讀自己的 INFORMATION_SCHEMA,
讓別人透過 http 來讀 json 就好, 但是這樣可能就得跑一下 apache 之類的.

後來我研究了一下, 公司內部本來就有一個 package, 可以透過 http 提供 mysql 各項的 metrics,

原本想要稍微修改這個 package, 讓我們想要提供的資料可以套用進去,

更深入研究之後, 發現這個 package 的 desc 寫著 mysql status framework,

Framework 唷, 真厲害, 而他真的就是一套用 perl 寫的簡單 framework,

用了 AnyEvent::HTTPD 做前端, 用了內部的 dbm 當 storage,
只要繼承他的 perl module, 寫一些 callback function,
就可以把怎樣搜集資料, 以及 http handler 都搞定,

立馬就寫了一下, 大概不到一百行就搞定我們要的功能,
也不會有 performance impact,

因為往 mysql 的 query 都不是即時的,
是透過 collector 每多久查一下, 寫到 storage,
前端的 AnyEvent::HTTPD 收到 request, 往 storage 查, 馬上就吐出去.


簡單, 好用, 又讓我覺得很爽.

工程師想要的是什麼? 就是這種偶然遇到的小 hack 得到的喜悅呀.

2012年11月14日 星期三

php spl_autoload

這兩天在 CI report 上, 一直回報某個 php class not found 的錯誤, 但是怎樣看,
那個 class 確實是有被 require 到, 只是用 get_declared_classes() 是看不到的,


現在很多 php framework, 或者 developer 都會叫人不要再用 require_once() 了,
大概也是這種原因吧.

不要再用 require_once(), 那要用什麼? 答案就是 spl_autoload.

用法其實很簡單, 宣告一個 register function,


function _autoload($class) {
    include $class . '.php';
}

spl_autoload_register('_autoload');

然後把這東西放在 auto_prepend_file 裡面, 就會在每個 request 進來的時候幫你把東西 load 進來了.

--
update: 要注意的是, class name ($class) 要跟檔名一樣, 剛才就遇到同事寫的

class name 是 getXXXX, 但是 filename 是 GetXXXX, 這樣他會抓不到.

2012年6月25日 星期一

traffic server with srv lookup.

lwhsu@ 後來又跟我重新講了一次 update.tw.freebsd.org 的狀況,

他是希望 proxy 在往後抓資料之前, 先查一次 dns srv record, 才決定到底該往哪台抓.

原本的想法是, 應該寫個 plugin 在 remap 之前作一下這事,

不過在查詢相關資料的時候, 意外的發現, 其實 ats 本身就可以做了,

code 就在那邊, 不過相關設定跟文件都沒有講的很清楚,

唯一的缺點是, 似乎不會按照 priority 去決定哪台,

--
CONFIG proxy.config.srv_enabled INT 1

在 record.config 中加上這行, remap.config 中把要 remap 的 target 改成要查的 hostname,
重跑 ats 就可以了.

2012年6月11日 星期一

使用 trafficserver 建立 mirror 的 FreeBSD portsnap site.

某長輩說目前的 portsnap.tw.freebsd.org 用 squid 跑, 某些原因, 要定期去清 cache,

我就想說, 那到底能不能直接用 trafficserver 來直接替換 squid, 單純跑 reverse proxy 呢?

答案是 ok 的, 雖然 traffic server 單純跑 reverse proxy 並不能顯示他的強項.



1: 在 www/trafficserver 下面打個 make install, 目前版本是 3.0.4 (stable)

2: 在 /etc/rc.conf or /etc/rc.conf.local 加上 "trafficserver_enable=YES"

3: 修改 /usr/local/etc/rc.d/trafficserver, 把 command & procname 裡面的 traffic_server 換成 traffic_manager

    這邊的理由是, traffic_server 並不能用 root 跑, 因為程式裡面會檢查,
    但是不用 root 跑, 他又不能 bind 在 port 1024 以下.
    解法就是跑 traffic_manager, 讓他帶起 traffic_server, 這樣就可以必掉一些安全性問題,
    又讓他 bind port 80.

4: 修改 /usr/local/etc/trafficserver/record.config
    1: CONFIG proxy.config.http.server_port INT 80 (讓 server 跑在 port 80)
    2: CONFIG proxy.config.http.cache.required_headers INT 0 (不管 response hearer 如何, 就是要 cache)

5: 修改 /usr/local/etc/trafficserver/remap.config
    加上

    map / http://portsnap.freebsd.org/

    這是指定 trafficserver 的 origin server 是 portsnap.freebsd.org

    其實應該不應該直接 remap '/', 而是要指定整個的 http schema 的,
    但是因為 portsnap 會用到 fetch, 而 fetch 並不多送 host header.

6: 修改 /usr/local/etc/trafficserver/storage.config
    把 256M 修改成想要的 cache size.

7: /usr/local/etc/rc.d/trafficserver start


這樣 local 就可以有一份 portsnap mirror 了.



portsnap -s 127.0.0.1 fetch 就可以作測試了.

--
UPDATE: 長輩說, 其實不是 portsnap 有問題, 而是 freebsd-update 會出問題, 但是其實是同一台, 所以要換就會整個一起換.

remap.config 這邊又研究了一下, 發現其實是 ok 的, 因為我偷懶, 沒跟著動 dns,

以 portsnap.tw.freebsd.org & update.tw.freebsd.org 這兩個 hostname 來講,

remap.config 設定如下.

map http://update.tw.freebsd.org/ http://update.freebsd.org/
map http://portsnap.tw.freebsd.org/ http://portsnap.freebsd.org/

設定好重跑 trafficserver 即可.

2012年5月13日 星期日

php embed or not.

雖然敝社還是在 layoff 的輪迴中, 但是總是沉浸在這樣的氣氛也不好,

5/24 有所謂的 hackday, 原本堅持不參加這種無聊節目的我, 剛好在 g+ 看到朋友討論 sketchup 有 embed ruby,

想到了 2007 年我曾經玩耍過 embed php, 那就來拿這個來玩耍一下吧.


hosted app 是 multi thread, 是所謂的 apache traffic server 的下一代, 也可以寫 plugin,

所以玩法就是把 php 用 plugin 的方式, embed 進這 hosted app 裡面,

掐頭掐尾, 把每次 request 中間, 要處理的資料丟進去 php runtime 處理,

(其實所謂的 plugin/module 的概念都差不了太多, 就是把中間某段切出來處理呀, 只是我試著改用別種語言來搞)


plugin 註冊的時候, load 進一個 php file, 裡面寫著遵守某些規範的 function,
傳什麼樣的資料進去, 離開的時候, 傳什麼樣的資料出來等規範.

然後就可以在每一次 request 之後, call php function 來幫你做事情.

想法很完美, 大概能省的 resource 也都省掉了, code 也寫好了,

基本測試也做完了, 跟差距的沒差太多, 不過一開始擔心的事情,

在第二次做 concurrent connection 的時候就發生,

由於敝社的 php 是 non thread, 被 embed 進 multi thread hosted app 中的時候,

同時間只有一個 connection, 一切都很美好,

但是只要同時有兩個 function 被 call, php runtime 一些 global variable 就會爆炸了.

而即使 php 是 thread, 這問題也是會發生, 因為 php_embed 這一個 sapi 實作上的問題,

他沒有把一些 global variable 切得很乾淨, 所以一定會爆炸.


查了一下 php mailing list, 其實 2009 年有人遇到這樣的問題, 所以某人就實作了

php_embed2, 號稱即使是 non-thread, 也一樣可以 embed 在 multi-thread hosted app 中.

測試的結果, fail.

Zend engine 跟我的 hosted app, 都會試著將 malloc/free 替換成他們自己的版本,

然後在 php runtime init 的時候, 就爆炸了, 根本不用等到 request 處理的瞬間.

所以, php embed + multi-thread hosted app FAILED.




既然都 fail 了有什麼好講?  我不管, 反正就是要 embed 個什麼東西進去,
手頭上幾個選項有 python, perl, lua.

敝社並不養雞, 所以會寫 python 的不多, perl 很多人用,
但是 default 的 perl5.8 沒有 thread, perl5.10 才有開, 但只有 32 bits binary,

意思就是我得強制我的 hosted app 也跑在 32bits, 我不要呀.


lua 則是聽過的人少, 會寫的更少, 不然會是最好的選擇,

選項都選光了, 一樣沒有好處理的, 怎麼辦? 我話都說出去了, 要參加呀.

好吧, 那 embed js 進去好了.


c++ daemon 收 request, 中間轉用 js 處理一些邏輯, 最後又用 c++ 收尾,
多棒呀.


講到 server side js, 大家一定會想到 nodejs, 但是其實並不是 embed nodejs.
因為 nodejs 目前只能寫 module 去 extend 他, 但還不接受被別人 embed,

要 embed, 請找 v8.

查了一下公司內的 package, v8 的版本有, 但是是 1.3.xxx, 現在 github 上的 v8 都已經 3.10.x 了, 也太老舊了吧.

v8 不行, 那 spidermonkey 總行了吧?

spidermonkey 有兩個版本, 1.7.0 & 1.8.5,

試了一下之後, 發現這兩個版本, 雖然 api level 是基本上相容的,
但是其實 1.8.x 整個 js runtime 似乎是用 c++ 整個重新實作過.

我不知道誰比較好, 但是看起來 1.8.x 有把一些 jit 之類的都做出來, 好像很厲害的樣子.

基本的 code 寫好, unittest 還沒做, concurrent connection 測試作過.

目前的測試, 有過 js runtime 跟沒過, 大概只差了一點點.


看起來是可以拿上場跟人較量了.

2012年3月18日 星期日

vim + clang_complete

最近寫了不少 c/c++ code, 如果有 vs ide like 的 auto complete 的話, 可以少很多工.

vim 下老牌的 auto complete 大概是 omni cpp complete 了吧, 不過唯一的缺點我覺得是, 要搭配 ctags 服用.


前兩天在 twitter 上看人講到 clang_complete 這套, 試用之後果然考試都考一百分了, 感覺十分爽快.


在 FreeBSD 下面要安裝很簡單, 首先你需要 vim with python support.


1: 到 /usr/ports/editors/vim-lite 下面重新安裝 vim,
    make WITH_PYTHON=yes install
2: 將 clang_complete clone 下來.
    git clone https://github.com/Rip-Rip/clang_complete.git
3: 確定有裝 clang; cd /usr/ports/lang/clang; make install
4: freebsd 9.0 之後, 系統預設就有 clang 了, 但是如果要更快點,
    這是不夠的, 因為相關 library 都被 static link 進 clang binary 中,
    最好是另外安裝 ports 裡面的版本.
5: 到 clone 下來的 clang_complete 下面, make install 就完成了.
    會看到相關的 code 都被安裝到 ~/.vim 下面.
6: 會感覺到慢吧? 因為他預設某些選項是關掉的,
    所以每次要 auto complete 的時候, 都會執行一下 clang,
    重新讀入所有要 parse 的 header files, 速度自然快不起來.
7: 在 .vimrc 下面加行 'let g:clang_use_library=1' 吧.
    這會改用 libclang 做 parse 的動作, 並且將結果 cache 在記憶體
    裡面, 你在該次的 vim session 中, 都會使用到 cache 裡的資料.