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 搭配使用。