基於go搭建微服務實踐教程 (二)

原文地址
轉載請註明原文及 翻譯地址

第二部分,咱們會:html

  • 創建go項目
  • 寫咱們的第一個微服務
  • 使用Gorilla組件爲HTTP請求提供JSON應答

咱們從微服務基礎開始,以後會搭建在咱們的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編譯器找尋源代碼和依賴關係的方式有關。
推薦閱讀: 
官方文檔
go path and workspaceweb

安裝SDK


在咱們寫代碼以前,咱們須要安裝go SDK。你能夠參照官方文檔spring

1. 創建根目錄


全部命令都是基於OS X或者Linux開發環境。若是你是在windows上開發,請調整這些命令。docker

mkdir ~/goworkspace
cd goworkspace
export GOPATH=`pwd`

這裏咱們創建一個根目錄而且讓環境變量GOPATH指向這個文件。這個根目錄會包含全部go的代碼和第三方庫。我建議你把GOPATH加入到.bash_profile中,因此你不須要每次都設置。數據庫

2. 建立第一個項目的文件夾和文件


如今咱們在根目錄,執行下面操做: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.go


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服務


讓項目更整潔,咱們會把全部的HTTP服務放進service文件夾中

啓動HTTP服務


建立一個文件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

這將開始測試,輸入參數是

  • user: 模擬的併發用戶數
  • duration: 測試持續秒數
  • baseUrl: 咱們測試服務的基礎路徑。當咱們用docker swarm時,baseUrl將會變成swarm的公共IP.

測試結束後,結果會寫入terminal中,同時一個漂亮的HTML報告會存入target/gatling/results/中

結果


以後咱們的基測將會在docker swarm中進行,如今,個人2014年的舊mac仍是要負重運行。
下面是內存消耗,在OS X的任務管理器中顯示

clipboard.png

1.2MB,還不錯。讓咱們用Gatling測試1K req/s.記住這是一個很是簡單的,只返回hard code的字符串的程序

clipboard.png

1k req/s讓accountservice消耗大約28MB的內存。這仍然是spring boot應用在開啓時的十分之一。這將是有趣的去看一下這個數字怎麼變當咱們加入一些真的處理函數進去。

性能和CPU使用

clipboard.png

1k req/s 消耗單核的8%左右

clipboard.png

不清楚Gatling怎樣處理毫秒的延遲,可是平均延遲是0ms.有一個請求用掉11ms。整體來看,accountservice性能至關好,能處理745 req/s。

接下來。。


下一部分,咱們要讓咱們的accountserive作一些有用的事。咱們要加入簡單的數據庫。咱們也會嘗試JSON序列化。同時檢查這些會怎樣影響消耗和性能。

相關文章
相關標籤/搜索