2010/08/31

Bonjour 你好

Bonjour蘋果zeroconf 免設定網路產品, 協助使用者更容易用網路。

雖然 Bonjour 與 zerconf 目標是各種網路服務, 這裡的範圍限制在網頁。免設定網路目前解決三個問題: 設備(名稱)的IP(網路位置)指定, 自動取得名稱與IP關連(廣播法 mDNS), 自動取得網路服務(dns-sd)。

技術上有點繞口, 實務上就如同一個 Wiki 工具 Voodoo Pad 寫妥許多內容, 要分享只要點選 Start 按鈕:
voodoopad_web.png

其他人利用 Safari 的書籤頁就可以看到:
safari_bonjour.png

不用網址、埠號、目錄, 看到名稱點下去, 立即瀏覽網頁。

除 Safari 外, Firefox 的 BonjourFoxy Plug-in 有相同功能, 目前支援 Windows 與 OSX。(Windows 安裝前須先裝妥 Apple Bonjour 套件)
bonjourfoxy.png

BonjourFoxy 另附有 Bonjour Browser 可觀察 dns-sd 詳細內容:
bonjour_browse.png

寫到這裡都是使用者操作面上。手上也有許多各式各樣網頁, 是否能建立 Bonjour 項目瀏覽資料?

最簡單方法大概利用 OSX 中 /usr/bin/dns-sd (Bonjour 套件) 在 Terminal 下指令:

$ dns-sd -R "Plone" _http._tcp "" 7070 "Plone 4 Here"

會佔住 Terminal, 要結束就直接按 ctrl-C。

dns-sd 除了為本機建立 Bonjour 服務項目, 也可代理(Proxy):

$ dns-sd -P "Zoo Keeper" _http._tcp local 80 hadoop.apache.org "" path=/zookeeper "你好 Zoo Keeper"

注意 path 可指定 URL 主機、埠號後的部份。

分享網址除了剪貼、美味書籤、社群工具, Bonjour 也是個方式, 而且是立即的。

2010/08/27

ZKFuse 與 macfuse

zkfuse 可能會被歸在玩具類, 是網路控制中心 ZooKeeper 把資料暴露在 FUSE 下。但與 macfuse 不符(API 差異), 就游進去解決一下 ...

參考網誌 HowTo Mount ZooKeeper using FUSE 的做法。

zkfuse 需要 fuse(macfuse 2.0.3), boost, log4cxx ... 都用 macports 裝妥。

取得 zookeeper-3.3.1.tar.gz 解開到 <zookeeper3>。

按照 /src/c/README 編 zookeeper:
$ cd zookeeper3
$ ant compile_jute
$ cd src/c/
$ ./configure
$ make
$ sudo make install
(預設安裝到 /usr/local/include/ 與 /usr/local/lib/)

到 zkfuse/ 目錄下:
$ cd ../contrib/zkfuse/
$ autoreconf -if
$ export LDFLAGS="-L/usr/local/lib -L/opt/local/lib"
$ export CXXFLAGS="-I/opt/local/include -I/usr/local/include"
$ export LIBS="-lzookeeper_mt"
$ ./configure

由於 macfuse 與 fuse 差異, 修改 src/mutex.h:
--- mutex.h.original    2010-08-27 10:38:10.000000000 +0800
+++ mutex.h 2010-08-27 10:38:20.000000000 +0800
@@ -34,7 +34,7 @@
public:
Mutex() {
pthread_mutexattr_init( &m_mutexAttr );
- pthread_mutexattr_settype( &m_mutexAttr, PTHREAD_MUTEX_RECURSIVE_NP );
+ pthread_mutexattr_settype( &m_mutexAttr, PTHREAD_MUTEX_RECURSIVE );
pthread_mutex_init( &mutex, &m_mutexAttr );
}
~Mutex() {

修改 src/zkfuse.cc:
--- zkfuse.cc.original  2010-08-27 10:28:37.000000000 +0800
+++ zkfuse.cc 2010-08-27 10:30:01.000000000 +0800
@@ -27,7 +27,7 @@

extern "C" {
#include
-#include
+// #include
}
#include
#include
@@ -4171,16 +4171,17 @@
int zkfuse_lock(const char *path, struct fuse_file_info *fi, int cmd,
struct flock *lock)
{
- (void) path;
- return ulockmgr_op(fi->fh, cmd, lock, &fi->lock_owner,
- sizeof(fi->lock_owner));
+ return 0;
+// (void) path;
+// return ulockmgr_op(fi->fh, cmd, lock, &fi->lock_owner,
+// sizeof(fi->lock_owner));
}

static
void init_zkfuse_oper(fuse_operations & fo)
{
- memset(&fo, 0, sizeof(fuse_operations));
+// memset(&fo, 0, sizeof(fuse_operations));
fo.getattr = zkfuse_getattr;
fo.fgetattr = zkfuse_fgetattr;
// fo.access = zkfuse_access;
@@ -4204,7 +4205,7 @@
fo.open = zkfuse_open;
fo.read = zkfuse_read;
fo.write = zkfuse_write;
- fo.statfs = zkfuse_statfs;
+// fo.statfs = zkfuse_statfs;
fo.flush = zkfuse_flush;
fo.release = zkfuse_release;
fo.fsync = zkfuse_fsync;

改完繼續做出 zkfuse:
$ make

產生 src/zkfuse ... 然後繼續: (開始用 zkfuse 前要先啟動 zookeeper)
$ mkdir 123
(要 mount 的入口點)
$ src/zkfuse -z localhost:2181 -m 123 -d
(要加 -d, 否則不能用 ... 但終端機會被佔住)

開另一個終端機看 123/ 目錄內容:
$ ls zookeeper3/src/contrib/zkfuse/123
zookeeper

2010/08/25

Avro 的使用與 Groovy Builder

Avro 是動態型別的資料序列化工具, 協助資料存放保留, 主要是讓網路化的應用程式有效率的溝通。如同使用語言設計模組溝通一般會透過界面方式, 在網路的服務常有著不同語言的實作, Avro 提供這些服務溝通的方式。

定義網路溝通的是通訊協定(Protocol), Avro 利用 JSON 定義通訊協定。

協定內包含有若干具名(name)的訊息(Protocol.Message), 每個訊息含有 3 種型態內容: 要求(Requst), 回覆(Response), 還有錯誤(Errors)。這些內容都以綱目(Schemas)定義內容的組成。

Avro 協助按綱目組成傳送的訊息。這裡有兩個關鍵: 一是組成, 一是傳送。

組成部份請參考 Patrick Hunt 先生的快速開始使用 Avro 範例 中可看出 python 與 ruby 都用語言本身的 collection 組成; 而 Java 則用 GenericData 套件。

傳送方面 Avro 有 HTTP 與 Socket 兩種通用方式, 服務端是 Server, 使用端是 Transceiver。如果用訊息服務(MOM)或搭配既有SOA,ESB系統, 就得抓訊息產生結果來操作。(在 Java 用 GenericRequestor, GenericResponder 中的 read/write 方法)

以 Groovy 與上面 Python 範例對連:
import org.apache.avro.Protocol
import org.apache.avro.util.Utf8
import org.apache.avro.ipc.HttpTransceiver
import org.apache.avro.generic.GenericData
import org.apache.avro.generic.GenericRequestor

def avpr = """
......
"""

def proto = Protocol.parse(avpr)

def trans = new HttpTransceiver(new URL('http://localhost:8080'))
def req = new GenericRequestor(proto, trans)
def message = new GenericData.Record(proto.getType('Message'))
message.put('to', new Utf8('wei'))
message.put('from', new Utf8('tcc'))
message.put('body', new Utf8('Hello 123'))
def params = new GenericData.Record(proto.messages.send.request);
params.put('message', message)
def ret = req.request('send', params)
println "Result: " + ret

其中 import 群下的 def avpr = """......""" 是把 src/avro/mail.avpr 定義的通訊協定拷貝進去。

如果不想用嵌入式, 可改為類似 python 範例中的:
def proto = Protocol.parse(new File('../avro/mail.avpr'))

再做 Groovy 的 Server:
import org.apache.avro.Protocol
import org.apache.avro.util.Utf8
import org.apache.avro.ipc.HttpServer
import org.apache.avro.generic.GenericData
import org.apache.avro.generic.GenericResponder

def avpr = """
......
"""

def proto = Protocol.parse(avpr)

class MyResponder extends GenericResponder {
public MyResponder(Protocol proto) {
super(proto)
}
public Object respond(Protocol.Message msg, Object req) {
return new Utf8("""Sent message ${req.message['to']}
from ${req.message['from']}
with body ${req.message['body']}""")
}
}
def res = new MyResponder(proto)

new HttpServer(res, 8080)

其中 respond(Protocol.Message msg, Object req) 的 req 為 GenericData.Record 在 Groovy 利用 GPath 直接操作相當便利。但客戶端用 GenericData 組成看起來頗辛苦, 想改用 Groovy Builder 式:
avro_builder.send { request {
message(to:'wei', from:'tcc', body:'Hello 123')
}}

寫個 AvroGroovyBuilder.java 放在 github。 (注意: 目前沒處理 List)

使用端就可簡化成為:
import org.apache.avro.Protocol
import org.apache.avro.ipc.HttpTransceiver
import org.apache.avro.generic.GenericRequestor

def avpr = """
......
"""

def proto = Protocol.parse(avpr)

def trans = new HttpTransceiver(new URL('http://localhost:8080'))
def req = new GenericRequestor(proto, trans)
def avro_builder = new org.tcchou.groovy.AvroGroovyBuilder(proto)
avro_builder.send { request {
message(to:'wei', from:'tcc', body:'Hello 123')
}}
def ret = req.request('send', avro_builder.params)
println "Result: " + ret

2010/08/10

Jailer 的使用

經常與資料庫為伍的開發者來說, 雖然 ORM 的普及解決大部分與 DB 溝通的問題, 但屬於型態上的。特別是常見的資訊系統, 很多工作直接與資料相關, 缺少實際資料根本是無以為繼。

以生產系統為例, 產品料表、流程定義與處置資料分由不同部門負責, 即便開發新的或調整 B2B, 缺乏這些資料還是無從下手。縱使 SA/SD 幾個單位走完, 最末還是要這些資料才好開工。

經歷過專案時程是上游廠商壓下游, 雛型只做一個段落就直接在正式環境運作 ... 邊進行、邊修程式、邊改資料的歲月真是折磨人。

前一陣子瀏覽到 http://jailer.sf.net 就看它閃耀著光輝。與資料整合(Data Integration)或 ETL 工具相比較, 這些傢私可能過大而不合用。

因為 jailer 只要關注兩件事: 首先定義表格(Tables)的相關性; 再來是抽取資料的條件。至於表格本身的定義 jailer 可抽取出來, 關連性雖然原則上可由鍵值決定, 但 jailer 也提供人工設定關連, 這點相當符合一般需求。

舉例來說 Order(1) -> Batch(N) ... 訂單關連到投產批是 1:N 關係; Batch(1) -> Job(N) ... 投產批與生產批也是 1:N 關係。其他與流程、處置、記錄相關資料零零總總, 這些關連都事先定義妥(大約有 10+ 個表格的關係)。日後只要指定投產批號, 就可拿到所有相關表格資料, 相當方便。

拿到資料的方式, 可選擇 SQL 直入測試資料庫; 或產生 Flat XML 與 http://www.dbunit.org 搭配使用。