原創文章,轉載請註明: 轉載自勤奮的小青蛙
本文連接地址: 【翻譯】使用Golang+MongoDB構建微服務javascript
翻譯來源:http://goinbigdata.com/how-to-build-microservice-with-mongodb-in-golang/html
參考連接:mgo驅動官網java
如今golang成爲熱門的微服務(RESTFul API)開發語言之一。一般,這些微服務系統採用了MongoDB做爲數據庫。在這篇博客裏,咱們將構建一個簡單的圖書管理微服務,採用Golang + MongoDB來完成該微服務。咱們將使用 mgo 來完成Golang與MongoDB的交互。git
MongoDB簡單,高可用性,而且是文檔類型的數據庫,也就是咱們常說的 NoSQL 數據庫。相比 SQL 型的數據庫,它的優點有:github
文檔只是一個由字段和值對組成的數據結構。字段的值可能包括其餘文檔,數組和文檔數組。MongoDB文檔相似於JSON對象,每一個文檔都做爲一個記錄存儲在MongoDB集合中。例如,一本書能夠表示爲如下文檔(json):golang
{ "isbn": "0134190440", "title": "The Go Programming Language", "authors": ["Alan A. A. Donovan", "Brian W. Kernighan"], "price": "$34.57" }
MongoDB在同一集合中存儲相似的文檔。例如:咱們將在圖書集合中存儲圖書文檔。其實MongoDB中的 collection 相似關係型數據庫中的表結構。不一樣之處在於 collection 不強制對文檔結構進行約束,不過咱們使用的時候儘可能把相同結構的文檔存儲在一個 collection 中。web
若是要從MongoDB獲取數據,則必須首先查詢。 Query是一組MongoDB概念,用於指定請求哪些數據的一組過濾器參數。MongoDB使用json和bson(binary json)來編寫查詢。獲取指定isbn的書籍的查詢示例可能以下所示:mongodb
{"isbn": "1234567"}
更多有關MongoDB的參考能夠訪問以下:數據庫
mgo(發音爲芒果)是Golang的MongoDB驅動程序。它的API很是簡單,遵循標準的Go語法。咱們將看到如何使用mgo幫助創建一個微服務器的CRUD(建立,讀取,更新,刪除)操做,但首先讓咱們熟悉會話管理。
session, err := mgo.Dial("localhost")
單個會話不容許併發處理,所以一般須要多個會話。得到另外一個會話的最快方法是複製現有的會話。確保使用後關閉它:
anotherSession := session.Copy() defer anotherSession.Close()
mgo與bson軟件包一塊兒使用,這簡化了寫入查詢。
c := session.DB("store").C("books") var books []Book err := c.Find(bson.M{}).All(&books)
c := session.DB("store").C("books") isbn := ... var book Book err := c.Find(bson.M{"isbn": isbn}).One(&book)
c := session.DB("store").C("books") err = c.Insert(book)
c := session.DB("store").C("books") err = c.Update(bson.M{"isbn": isbn}, &book)
c := session.DB("store").C("books") err := c.Remove(bson.M{"isbn": isbn})
如下是由Go編寫並由MongoDB支持的book store microservice的完整示例。您能夠從GitHub下載該示例。
該微服務使用 Goji 來提供路由功能,能夠查看這篇博客進行詳細瞭解: How to write RESTful services with Goji
下面是圖書管理的微服務核心代碼:
package main import ( "encoding/json" "fmt" "log" "net/http" "goji.io" "goji.io/pat" "gopkg.in/mgo.v2" "gopkg.in/mgo.v2/bson" ) func ErrorWithJSON(w http.ResponseWriter, message string, code int) { w.Header().Set("Content-Type", "application/json; charset=utf-8") w.WriteHeader(code) fmt.Fprintf(w, "{message: %q}", message) } func ResponseWithJSON(w http.ResponseWriter, json []byte, code int) { w.Header().Set("Content-Type", "application/json; charset=utf-8") w.WriteHeader(code) w.Write(json) } type Book struct { ISBN string `json:"isbn"` Title string `json:"title"` Authors []string `json:"authors"` Price string `json:"price"` } func main() { session, err := mgo.Dial("localhost") if err != nil { panic(err) } defer session.Close() session.SetMode(mgo.Monotonic, true) ensureIndex(session) mux := goji.NewMux() mux.HandleFunc(pat.Get("/books"), allBooks(session)) mux.HandleFunc(pat.Post("/books"), addBook(session)) mux.HandleFunc(pat.Get("/books/:isbn"), bookByISBN(session)) mux.HandleFunc(pat.Put("/books/:isbn"), updateBook(session)) mux.HandleFunc(pat.Delete("/books/:isbn"), deleteBook(session)) http.ListenAndServe("localhost:8080", mux) } func ensureIndex(s *mgo.Session) { session := s.Copy() defer session.Close() c := session.DB("store").C("books") index := mgo.Index{ Key: []string{"isbn"}, Unique: true, DropDups: true, Background: true, Sparse: true, } err := c.EnsureIndex(index) if err != nil { panic(err) } } func allBooks(s *mgo.Session) func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) { session := s.Copy() defer session.Close() c := session.DB("store").C("books") var books []Book err := c.Find(bson.M{}).All(&books) if err != nil { ErrorWithJSON(w, "Database error", http.StatusInternalServerError) log.Println("Failed get all books: ", err) return } respBody, err := json.MarshalIndent(books, "", " ") if err != nil { log.Fatal(err) } ResponseWithJSON(w, respBody, http.StatusOK) } } func addBook(s *mgo.Session) func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) { session := s.Copy() defer session.Close() var book Book decoder := json.NewDecoder(r.Body) err := decoder.Decode(&book) if err != nil { ErrorWithJSON(w, "Incorrect body", http.StatusBadRequest) return } c := session.DB("store").C("books") err = c.Insert(book) if err != nil { if mgo.IsDup(err) { ErrorWithJSON(w, "Book with this ISBN already exists", http.StatusBadRequest) return } ErrorWithJSON(w, "Database error", http.StatusInternalServerError) log.Println("Failed insert book: ", err) return } w.Header().Set("Content-Type", "application/json") w.Header().Set("Location", r.URL.Path+"/"+book.ISBN) w.WriteHeader(http.StatusCreated) } } func bookByISBN(s *mgo.Session) func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) { session := s.Copy() defer session.Close() isbn := pat.Param(r, "isbn") c := session.DB("store").C("books") var book Book err := c.Find(bson.M{"isbn": isbn}).One(&book) if err != nil { ErrorWithJSON(w, "Database error", http.StatusInternalServerError) log.Println("Failed find book: ", err) return } if book.ISBN == "" { ErrorWithJSON(w, "Book not found", http.StatusNotFound) return } respBody, err := json.MarshalIndent(book, "", " ") if err != nil { log.Fatal(err) } ResponseWithJSON(w, respBody, http.StatusOK) } } func updateBook(s *mgo.Session) func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) { session := s.Copy() defer session.Close() isbn := pat.Param(r, "isbn") var book Book decoder := json.NewDecoder(r.Body) err := decoder.Decode(&book) if err != nil { ErrorWithJSON(w, "Incorrect body", http.StatusBadRequest) return } c := session.DB("store").C("books") err = c.Update(bson.M{"isbn": isbn}, &book) if err != nil { switch err { default: ErrorWithJSON(w, "Database error", http.StatusInternalServerError) log.Println("Failed update book: ", err) return case mgo.ErrNotFound: ErrorWithJSON(w, "Book not found", http.StatusNotFound) return } } w.WriteHeader(http.StatusNoContent) } } func deleteBook(s *mgo.Session) func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) { session := s.Copy() defer session.Close() isbn := pat.Param(r, "isbn") c := session.DB("store").C("books") err := c.Remove(bson.M{"isbn": isbn}) if err != nil { switch err { default: ErrorWithJSON(w, "Database error", http.StatusInternalServerError) log.Println("Failed delete book: ", err) return case mgo.ErrNotFound: ErrorWithJSON(w, "Book not found", http.StatusNotFound) return } } w.WriteHeader(http.StatusNoContent) } }
curl是構建和測試RESTful微服務的不可或缺的工具。curl也是RESTful API開發文檔中常常使用的命令,以提供API調用演示。
演示
請求:
curl -X POST -H "Content-Type: application/json" -d @body.json http://localhost:8080/books body.json: { "isbn": "0134190440", "title": "The Go Programming Language", "authors": ["Alan A. A. Donovan", "Brian W. Kernighan"], "price": "$34.57" }
響應:
1 |
|
演示:
請求:
curl -H "Content-Type: application/json" http://localhost:8080/books
響應:
200 OK [ { "ISBN": "0134190440", "Title": "The Go Programming Language", "Authors": [ "Alan A. A. Donovan", "Brian W. Kernighan" ], "Price": "$34.57" }, { "ISBN": "0321774639", "Title": "Programming in Go: Creating Applications for the 21st Century (Developer's Library)", "Authors": [ "Mark Summerfield" ], "Price": "$31.20" } ]
演示:
請求:
curl -H "Content-Type: application/json" http://localhost:8080/books/0134190440
響應:
200 OK { "ISBN": "0134190440", "Title": "The Go Programming Language", "Authors": [ "Alan A. A. Donovan", "Brian W. Kernighan" ], "Price": "$34.57" }
演示:
請求:
curl -X PUT -H "Content-Type: application/json" -d @body.json http://localhost:8080/books/0134190440 body.json: { "isbn": "0134190440", "title": "The Go Programming Language", "authors": ["Alan A. A. Donovan", "Brian W. Kernighan"], "price": "$20.00" }
響應:
204 No Content
演示:
請求:
curl -X DELETE -H "Content-Type: application/json" -d @body.json http://localhost:8080/books/0134190440
響應:
204 No Content
MongoDB是一個很是受歡迎的後端,用於使用Go編寫微服務器。Go(mgo)的MongoDB驅動程序是最經常使用的,並且很是易於使用。若是您正在構建,測試或記錄RESTful服務,請不要忽略curl工具。
原創文章,轉載請註明: 轉載自勤奮的小青蛙
本文連接地址: 【翻譯】使用Golang+MongoDB構建微服務