基于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序列化。同时检查这些会怎样影响消耗和性能。

相关文章
相关标签/搜索