原文地址
轉載請註明原文及 翻譯地址
第二部分,咱們會:html
咱們從微服務基礎開始,以後會搭建在咱們的docker swarm上git
對於內部請求仍是外部請求,經過HTTP提供JSON應答不是惟一的選擇。但咱們在這裏會主要講解這一方法。當內部請求或者外部請求也是一個系統時,用RPC和二進制信息格式做爲請求方式也是一個不錯的選擇,例如protocol buffers。go有內置的RPC支持,gRPC值得研究一下。然而,這裏咱們先講解用內置的http package和Gorilla web tookit。github
許多有用的框架(安全,追蹤)依賴於HTTP頭信息來傳遞請求的狀態,這也是用HTTP的好處。在咱們以後的博客中也會看到咱們傳遞相關的ID和OAuth頭信息。雖然其餘的協議也支持類似的功能,可是許多框架實在HTTP上開發的,因此我儘可能用他們來讓咱們的集成更直接。golang
若是你已經熟悉go開發,你能夠略過這一段。
我以爲go的工做區須要一點時間來熟悉他。我習慣於用個人項目的根目錄做爲工做區的根目錄。然而go組織一個工做區的方法有點奇怪,這和go編譯器找尋源代碼和依賴關係的方式有關。
推薦閱讀:
官方文檔
go path and workspaceweb
在咱們寫代碼以前,咱們須要安裝go SDK。你能夠參照官方文檔spring
全部命令都是基於OS X或者Linux開發環境。若是你是在windows上開發,請調整這些命令。docker
mkdir ~/goworkspace cd goworkspace export GOPATH=`pwd`
這裏咱們創建一個根目錄而且讓環境變量GOPATH指向這個文件。這個根目錄會包含全部go的代碼和第三方庫。我建議你把GOPATH加入到.bash_profile中,因此你不須要每次都設置。數據庫
如今咱們在根目錄,執行下面操做:json
mkdir -p src/github.com/callistaenterprise
若是你想本身跟着打代碼,執行下面操做:segmentfault
cd src/github.com/callistaenterprise mkdir -p goblog/accountservice cd goblog/accountservice touch main.go mkdir service
或者你能夠直接clone這個git倉庫,轉到p2分支。在文件夾src/github.com/callistaenterprise中,執行
git clone https://github.com/callistaenterprise/goblog.git cd goblog git checkout P2
如今咱們能夠開始了。
main函數和其餘語言中的同樣,程序的接入點。讓咱們寫一些代碼來運行一下
package main import ( "fmt" ) var appName = "accountservice" func main() { fmt.Printf("Starting %v\n", appName) }
如今,運行它。肯定你在這個文件夾下:
$GOPATH/src/github.com/callistaenterprise/goblog/accountservice
> go run *.go Starting accountservice >
完成,這段代碼打印以後退出,如今讓咱們加上HTTP終端
讓項目更整潔,咱們會把全部的HTTP服務放進service文件夾中
建立一個文件webserve.go在/services文件夾中
package service import ( "net/http" "log" ) func StartWebServer(port string) { log.Println("Starting HTTP service at " + port) err := http.ListenAndServe(":" + port, nil) // Goroutine will block here if err != nil { log.Println("An error occured starting HTTP listener at port " + port) log.Println("Error: " + err.Error()) } }
咱們用內置的net/http包來執行ListenAndServe,從而在指定端口開啓HTTP服務。
更新main.go用一個寫死的端口去請求StartWebServer函數
package main import ( "fmt" "github.com/callistaenterprise/goblog/accountservice/service" // NEW ) var appName = "accountservice" func main() { fmt.Printf("Starting %v\n", appName) service.StartWebServer("6767") // NEW }
運行程序:
> go run *.go Starting accountservice 2017/01/30 19:36:00 Starting HTTP service at 6767
如今咱們有一個簡單的HTTP服務監聽6767端口,curl這個服務
curl http://localhost:6767 404 page not found
404是咱們預料到的,由於咱們尚未定義任何routes
中止這個服務-Ctrl+C
咱們要開始搞事情了,咱們定義第一個路由。在service文件夾中,建立routes.go
package service import "net/http" // Defines a single route, e.g. a human readable name, HTTP method and the // pattern the function that will execute when the route is called. type Route struct { Name string Method string Pattern string HandlerFunc http.HandlerFunc } // Defines the type Routes which is just an array (slice) of Route structs. type Routes []Route // Initialize our routes var routes = Routes{ Route{ "GetAccount", // Name "GET", // HTTP method "/accounts/{accountId}", // Route pattern func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json; charset=UTF-8") w.Write([]byte("{\"result\":\"OK\"}")) }, }, }
上面代碼,咱們定義路徑/accounts/{accountsId},以後咱們能夠curl這個路徑。Gorilla也支持複雜的路由包括正則匹配,schemes,方法,queries,頭信息值等。因此不僅限於路徑和路徑參數
如今,咱們會返回一個JSON信息:
{"result":"OK"}
咱們如今須要一些樣板代碼來啓動Gorilla路由。在service文件夾中,建立router.go
package service import ( "github.com/gorilla/mux" ) // Function that returns a pointer to a mux.Router we can use as a handler. func NewRouter() *mux.Router { // Create an instance of the Gorilla router router := mux.NewRouter().StrictSlash(true) // Iterate over the routes we declared in routes.go and attach them to the router instance for _, route := range routes { // Attach each route, uses a Builder-like pattern to set each route up. router.Methods(route.Method). Path(route.Pattern). Name(route.Name). Handler(route.HandlerFunc) } return router }
在import中咱們定義一個依賴 github.com/gorilla/mux。
爲了讓以上文件編譯及運行,咱們須要用go get來獲取定義的package
> go get
go工具會下載全部的源代碼。以後這些代碼會存儲在$GOPATH/src/github.com/gorilla/mux的本地文件中。並編譯到你的靜態連接二進制文件。
如今,從新看webserver.go並加入下面代碼:
func StartWebServer(port string) { r := NewRouter() // NEW http.Handle("/", r) // NEW
這段代碼吧咱們剛剛寫的路由加入http.Handle中。讓咱們運行代碼:
> go run *.go Starting accountservice 2017/01/31 15:15:57 Starting HTTP service at 6767
curl這個地址
> curl http://localhost:6767/accounts/10000 {"result":"OK"}
咱們第一個服務就作好了!
因爲咱們在探索基於go的微服務的資源消耗和效率,咱們作一個基測。我開發了一個簡單的Gatling測試會用get方法請求accounts/{accountId}。若是你check out這部分的代碼,你能看到測試在goblog/loadtest文件夾中.
若是你想本身測試,確保accountservice正在你的localhost中運行。而且你已經checked out分支P2。你須要有Jave runtime和Apache Maven.
去文件夾/goblog/loadtest中執行下面的命令
> mvn gatling:execute -Dusers=1000 -Dduration=30 -DbaseUrl=http://localhost:6767
這將開始測試,輸入參數是
測試結束後,結果會寫入terminal中,同時一個漂亮的HTML報告會存入target/gatling/results/中
以後咱們的基測將會在docker swarm中進行,如今,個人2014年的舊mac仍是要負重運行。
下面是內存消耗,在OS X的任務管理器中顯示
1.2MB,還不錯。讓咱們用Gatling測試1K req/s.記住這是一個很是簡單的,只返回hard code的字符串的程序
1k req/s讓accountservice消耗大約28MB的內存。這仍然是spring boot應用在開啓時的十分之一。這將是有趣的去看一下這個數字怎麼變當咱們加入一些真的處理函數進去。
1k req/s 消耗單核的8%左右
不清楚Gatling怎樣處理毫秒的延遲,可是平均延遲是0ms.有一個請求用掉11ms。整體來看,accountservice性能至關好,能處理745 req/s。
下一部分,咱們要讓咱們的accountserive作一些有用的事。咱們要加入簡單的數據庫。咱們也會嘗試JSON序列化。同時檢查這些會怎樣影響消耗和性能。