golang實現rest server架構(一)

來源:互聯網
上載者:User

第一篇:用golang對資料庫標準操作進行封裝(mysql)

背景

用golang對資料庫標準操作進行封裝,為後面的rest server提供資料庫訪問層。實現的目標是:能根據rest請求參數自動產生資料庫動作陳述式,提供增、刪、改、查、批量寫入、事務等必要的資料庫操作封裝。並可以方便的擴充到多種資料庫,讓所有的資料庫操作對於rest server來說表現為一致的提供者。

一些關鍵點

  1. 介面設計做到恰到好處,夠用且不繁雜。
  2. 函數參數的設計,go不支援函數重載,如何善用interface{}。
  3. 用map[string]interface{}來處理rest的json請求參數,並自動產生相應的sql。
  4. 資料庫查詢結果能方便的轉化為json,讓rest server返回給使用者。

代碼解析

按功能模組對核心代碼進行說明

IBock.go

資料庫標準操作介面定義,根據我的實踐經驗,以下的介面設計已經能夠很好的支援大部分的資料庫操作,這些操作包括了根據json參數自動完成的CURD、手寫sql支援、批量插入(更新)心及事務操作。
type IBock interface{    //根據參數,自動完成資料庫查詢    Retrieve(params map[string]interface{}, args ...interface{}) map[string]interface{}    //根據參數,自動完成資料庫插入    Create(params map[string]interface{}, args ...interface{}) map[string]interface{}    //根據參數,自動完成資料庫更新(只支援單條)    Update(params map[string]interface{}, args ...interface{}) map[string]interface{}    //根據參數,自動完成資料庫刪除(只支援單條)    Delete(params map[string]interface{}, args ...interface{}) map[string]interface{}    //手寫查詢sql支援    QuerySql(sql string, values []interface{}, params map[string]interface{}) map[string]interface{}    //手寫非查詢sql支援    ExecSql(sql string, values []interface{}) map[string]interface{}    //批量插入或更新    InsertBatch(tablename string, els []interface{}) map[string]interface{}    //事務支援    TransGo(objs map[string]interface{}) map[string]interface{}}
參數說明
  • params, 對應rest server接收到使用者資料,由json對象轉換而來。
  • args,這個參數的目標是接收id(資訊ID),fields(表欄位數組),session(使用者session)這三個參數,這樣做的初衷是既要統一介面函數形式,又可以在編碼時少傳入作為點位符的nil
  • values,為sql查詢參數化提供的參數列表
  • els,批量插入的每一行資料對象集
  • objs,事務對象集
  • 返回參數為go的映射,很容易轉化為json。

Bock.go

介面的具體實現,本文是對mysql的實現,暫只實現了基本的CURD,項目中會逐步完善。
//我們把操作對象定義在一個表上type Bock struct {    Table string}//parseArgs函數的功能是解析args參數中包括的可變參數,實現在下面func (b *Bock) Retrieve(params map[string]interface{}, args ...interface{}) map[string]interface{} {    //查詢時我們一般只關注查詢哪些表欄位    _, fields, _ := parseArgs(args)    //調用具體的查詢介面,查詢介面將根據輸入參數params自動實現sql查詢語句,支援多樣的查詢定義,如:lks(從多個字型查詢相同內容),ors(或查詢),ins(in查詢)等    return Query(b.Table, params, fields)}func (b *Bock) Create(params map[string]interface{}, args ...interface{}) map[string]interface{} {    //建立介面,一般都會關注使用者在session的ID    _, _, session := parseArgs(args)    uId := session["userid"].(string)    params["u_id"] = uId    //調用具體的插入介面    return Insert(b.Table, params)}func (b *Bock) Update(params map[string]interface{}, args ...interface{}) map[string]interface{} {    //只支援單個更新,所以ID必須存在    id, _, _ := parseArgs(args)    if len(id) == 0 {        rs := make(map[string]interface{})        rs["code"] = 301        rs["err"] = "Id must be input."        return rs    }    return Update(b.Table, params)}func (b *Bock) Delete(params map[string]interface{}, args ...interface{}) map[string]interface{} {    //只支援單個刪除,所以ID必須存在    id, _, _ := parseArgs(args)    if len(id) == 0 {        rs := make(map[string]interface{})        rs["code"] = 301        rs["err"] = "Id must be input."        return rs    }    return Delete(b.Table, params)}
parseArgs函數的實現
func parseArgs(args []interface{}) (string, []string, map[string]interface{}) {    //解析指定的參數    var id string                                //資訊ID    var fields []string                          //查詢欄位集    var session map[string]interface{}           //使用者session對象    for _, vs := range args {        switch vs.(type) {        case map[string]interface{}:            //只接收指定類型            for k, v := range vs.(map[string]interface{}) {                if k == "id" {                    id = v.(string)                }                if k == "fields" {                    fields = v.([]string)                }                if k == "session" {                    session = v.(map[string]interface{})                }            }        default:        }    }    return id, fields, session    //返回解析成功的參數}

Helper.go

資料操作的具體實現,大多是虛擬碼,項目後續會逐步完善,查詢介面最重要,後面會有單獨文章進行解析
func Query(tablename string, params map[string]interface{}, fields []string ) map[string]interface{} {    //調用具體實現的私用函數,介面中分自動和手動兩個函數,在私用函數中屏蔽差異內聚功能    return query(tablename, params, fields, "", nil)}func Insert(tablename string, params map[string]interface{}) map[string]interface{} {    sql := "Insert into " + tablename    values := make([]interface{},0)    return execute(sql, values)}func Update(tablename string, params map[string]interface{}) map[string]interface{} {    sql := "Update " + tablename + " set "    values := make([]interface{},0)    return execute(sql, values)}func Delete(tablename string, params map[string]interface{}) map[string]interface{} {    sql := "Delete from " + tablename + " where"    values := make([]interface{},0)    return execute(sql, values)}
私用查詢函數定義
//五個輸入參數,分別適配自動與手動查詢func query(tablename string, params map[string]interface{}, fields []string, sql string, vaules []interface{}) map[string]interface{} {    if vaules == nil {        vaules = make([]interface{},0)    }    //調用真正的資料庫操作函數    return execQeury("select "+ strings.Join(fields, ",")+" from " + tablename, vaules)}
非查詢類具體操作函數
//因為golang把有結果集的和無結果集的操作是分開的,不象在java或node.js中,可以有進階函數進行統一操作,只能分開。func execute(sql string, values []interface{}) map[string]interface{}  {    //返回json對象,以map形式表達    rs := make(map[string]interface{})    rs["code"] = 200    return rs}
查詢類具體操作(已經實現),結果集以json對象封裝,儲存在map中
func execQeury(sql string, values []interface{}) map[string]interface{}  {    var configs interface{}    ...//省略資料配置擷取代碼,請參照以前的文章    dao, err := mysql.Open(dialect, dbUser + ":"+dbPass+"@tcp("+dbHost+":"+dbPort+")/"+dbName+"?charset="+dbCharset)    stmt, err := dao.Prepare(sql)    rows, err := stmt.Query(values...)    columns, err := rows.Columns()       //取出欄位名稱    vs := make([]mysql.RawBytes, len(columns))    scans := make([]interface{}, len(columns))    for i := range vs {                 //預設取值地址        scans[i] = &vs[i]    }    var result []map[string]interface{}    for rows.Next() {        _ = rows.Scan(scans...)        //塡入一列值        each := make(map[string]interface{})        for i, col := range vs {            if col != nil {                each[columns[i]] = string(col)        //增值            }else{                each[columns[i]] = nil            }        }        result = append(result, each)    }    rs["code"] = 200    //data, _ := json.Marshal(result)            //這樣就能轉換為json    rs["rows"] = result    return rs}
資料庫的大量操作,在前面的文章中已經用golang實現,只是還未封裝,有興趣的朋友可以看我前面的文章。

bock.go(程式入口)

最終目標的入口將是一個網路服務,提供標準的restful服務,現在只是用來測試,再這說明一下願景。
    table := Bock.Bock{                    //上體執行個體        Table: "role",                     //對role表時行操作    }    var params map[string] interface{}     //類比json參數    args := make(map[string] interface{})  //其它參數    db := make([]DB.IBock, 1)              //對介面編程    db[0] = &table                         //介面指向執行個體對象,這裡可以現時處理多個不同的執行個體    fields := []string {"id", "name"}    args["fields"] = fields    rs, _ := db[0].Retrieve(params, args)  //在這可以迴圈處理多個不同的執行個體,我們最終的目標就是在這接受使用者的http請求,由路由自動分發不同的請求,我們的資料庫封裝自動產生sql陳述式完成使用者的基本需求。    fmt.Println(rs)

項目地址

https://github.com/zhoutk/goTools

使用方法

git clone https://github.com/zhoutk/goToolscd goToolsgo getgo run bock.gogo buid bock.go./bock        

小結

經過多種方案的對比,發現go語言作為網路服務的吞吐率是最棒的,所以有了將以往在其它平台上的經驗(node.js,java,python3),用go來實現,期望有驚喜,寫代碼我是認真的。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.