Skip to content

lexkong/KitSvc

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

268 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

已棄用 DEPRECATED

此專案僅供研究或結構參考用途。


kitsvc

KitSvc 是一個 Go 的單個微服務初始包
提供了 Gin、Consul、Prometheus、EventStore 相關模塊和 Gorm 與 NSQ。(建議搭配 KitGate

特色

  • 採用 Gin 框架,以一般網站應用程式的方式處理請求。
  • 具獨立性的微服務架構
  • 透過 Gorm 與資料庫連線
  • Consul、Prometheus、NSQ、EventStore
  • Melody 作為支援 WebSocket 的框架
  • 內建預設範例(亦有 WebSocket、NSQ 與 EventStore 和 JSON Web Token 的使用方式)

主要結構

KitSvc 是極具獨立性的微服務初始包,能作為單個微服務。基本使用方式與一般網站應用程式相同,但多了數個專門配合微服務的工具。

KitSvc
├── client               // 客戶端
├── errno                // 錯誤代碼
├── model                // 資料結構
├── module
  ├── event
     ├── eventstore    // 事件邏輯
     └── *event.go     // 事件函式介面
  ├── metrics           // 效能測量
  ├── mq
     ├── mqstore       // 訊息佇列邏輯
     └── *mq.go        // 訊息佇列函式介面
  └── sd                // 服務探索
├── router
  ├── middleware        // 中介軟體
  └── *router.go        // 路由
├── server               // 主要邏輯
  └── *main_test.go     // 單元測試
├── service              // 主要邏輯
├── shared               // 資源庫、共享函式、工具
├── store
  ├── datastore         // 資料庫邏輯
  └── *store.go         // 資料庫函式介面
├── vendor               // 依賴性套件
└── version              // 版本

依賴性

KitSvc 依賴下列服務,請確保你有安裝。

環境變數設置

為了方便在 Docker 上部署,KitSvc 沒有設定檔,而是透過環境變數來設置。下面這些設置都是預設值,如果你的環境符合這些預設值那麼你就不需要手動宣告,可以直接啟動程式進行測試。

# 服務名稱
KITSVC_NAME="Service"
# 服務暴露網址
KITSVC_URL="http://127.0.0.1:8080"
# 服務位置
KITSVC_ADDR="127.0.0.1:8080"
# 服務埠口
KITSVC_PORT=8080
# 服務註釋
KITSVC_USAGE="Operations about the users."
# JSON Web Token 的加密密碼
KITSVC_JWT_SECRET="4Rtg8BPKwixXy2ktDPxoMMAhRzmo9mmuZjvKONGPZZQSaJWNLijxR42qRgq0iBb5"
# Ping 伺服器的最大嘗試次數
KITSVC_MAX_PING_COUNT=20
# 除錯模式
KITSVC_DEBUG=false

# 資料庫驅動
KITSVC_DATABASE_DRIVER="mysql"
# 資料庫名稱
KITSVC_DATABASE_NAME="service"
# 資料庫主機位置與埠口
KITSVC_DATABASE_HOST="127.0.0.1:3306"
# 資料庫帳號
KITSVC_DATABASE_USER="root"
# 資料庫密碼
KITSVC_DATABASE_PASSWORD="root"
# 資料庫字符集
KITSVC_DATABASE_CHARSET="utf8"
# 資料庫時間地區
KITSVC_DATABASE_LOC="Local"
# 是否解析時間
KITSVC_DATABASE_PARSE_TIME=true

# 訊息產生者位置
KITSVC_NSQ_PRODUCER="127.0.0.1:4150"
# 訊息產生者的 HTTP 位置
KITSVC_NSQ_PRODUCER_HTTP="127.0.0.1:4151"
# 訊息中心位置(以無空白 `,` 逗號新增多個位置)
KITSVC_NSQ_LOOKUPS="127.0.0.1:4161"

# 事件存儲中心的 HTTP 位置
KITSVC_ES_SERVER_URL="http://127.0.0.1:2113"
# 事件存儲中心帳號
KITSVC_ES_USERNAME="admin"
# 事件存儲中心密碼
KITSVC_ES_PASSWORD="changeit"

# 紀錄的命名空間
KITSVC_PROMETHEUS_NAMESPACE="service"
# 紀錄的服務名稱
KITSVC_PROMETHEUS_SUBSYSTEM="user"

# 服務中心的健康檢查時間
KITSVC_CONSUL_CHECK_INTERVAL="30s"
# 服務中心的健康檢查逾時時間
KITSVC_CONSUL_CHECK_TIMEOUT="1s"
# 服務中心的服務標籤(以無空白 `,` 逗號新增多個位置)
KITSVC_CONSUL_TAGS="user,micro"

模塊介紹

  • 資料庫(Database):一個微服務擁有一個資料庫,同屬性的微服務可共享同一個資料庫,在這裡我們採用 Gorm 與資料庫連線。
  • 服務探索(Discovery):向 Consul 服務中心註冊,表示自己可供使用,此舉是利於負載平衡做相關處理。
  • 效能測量(Instrumenting):每個函式的執行時間、呼叫次數都可以被測量,並傳送給 Prometheus 伺服器彙整成視覺化資料。
  • 紀錄層(Logging):傳入、輸出請求都可以被記錄,最終可以儲存程記錄檔供未來除錯。
  • 訊息傳遞(Messaging):微服務之間並不會直接溝通,但可以透過 NSQ 訊息中心廣播讓另一個微服務處理相關事情,且無需等待該微服務處理完畢(即異步處理,不會阻擋)。
  • 事件中心(EventStore):微服務之間可以發送、監聽事件,並在重新上線時重播事件找回資料庫的內容。

非此套件

這些功能會在 KitGate 中實作,而不是 KitSvc。

  • 版本控制(Versioning):能夠將舊的微服務替換掉,並且無須停機即可升級至新版本服務。
  • 速率限制(Rate Limiting):避免客戶端在短時間內發送大量請求而導致癱瘓。

未加入

在 KitSvc 中這些功能沒有被加入,可能是計畫中。

  • 斷路器(Circuit Breaker):斷路器能在服務錯誤時果斷拒絕外來請求,並給予一定的時間回復作業。

從這開始

請注意,KitSvc 並不能直接透過 go get 取得,因此你需要手動 git clone 一份回家。順帶記得的是:KitSvc 是一個開發用的模板,而不是套件。

# 設置環境變數。
$ export PATH=$PATH:$GOPATH/bin

# 從 Git 上複製一份此倉庫回家。
$ git clone git@github.com:TeaMeow/KitSvc.git $GOPATH/src/github.com/TeaMeow/KitSvc
$ cd $GOPATH/src/github.com/TeaMeow/KitSvc

可用指令

KitSvc 有下列指令可供開發環境時使用。

make deps                   # 將依賴性套件複製進 GOPATH
make test                   # 進行單元測試
make test_mysql test_sqlite # 測試資料庫功能
make run                    # 建置並執行程式
make build                  # 建置程式

如果有其他問題可以參考 .drone.yml 檔案,該檔案為自動測試環境的設置檔,你可以以此作為 KitSvc 所需的環境、設定依據。

索引

Client

// GetUser gets an user by the user identifier.
func (c *client) GetUser(username string) (out *model.User, err error) {
	resp, err := grequests.Get(uri(pathSpecifiedUser, c.base, username), &grequests.RequestOptions{})
	resp.JSON(&out)
	return
}

/client 是客戶端,在這裡我們會撰寫透過 HTTP 和自己的程式溝通的函式。當需要進行單元測試時,我們就可以呼叫客戶端來測試自己的伺服器。如此一來就可以免去透過 Postman 逐一測試 API 的困擾。

Errno

var (
	ErrBind = &Err{
		Code:       "BIND_ERR",
		Message:    "Error occurred while binding the request body to the struct.",
		StatusCode: http.StatusBadRequest}
)

/errno 裡面存放的是錯誤回傳代碼,我們會在這裡定義程式的錯誤代碼、訊息、HTTP 狀態碼。之後發生錯誤時就可以回傳這個代碼。

Model

// User represents a registered user.
type User struct {
	ID       int    `json:"id"`
	Username string `json:"username" gorm:"not null" binding:"required" validate:"min=1,max=32"`
	Password string `json:"password" gorm:"not null" binding:"required" validate:"min=8,max=128"`
}

// Compare with the plain text password. Returns true if it's the same as the encrypted one (in the `User` struct).
func (u *User) Compare(pwd string) (err error) {
	err = auth.Compare(u.Password, pwd)
	return
}

/model 就像是 MVC 結構中的 Model,但這裡存放的是物件定義,例如使用者建構體、文章建構體,我們亦會在這個地方替這些建構體新增一些輔助函式。

Event

var (
	EvtUserCreated = "user_created"
	EvtUserDeleted = "user_deleted"
)

/module/event 存放著我們會在此服務所用到的所有事件代碼,詳細的事件廣播範例請參考下方的 /service 段落。

MQ

var (
	MsgSendMail = "send_mail"
)

/module/mq 存放著我們在此服務會用到的訊息主題代碼,詳細的發布訊息範例請參考下方的 /service 段落。

Router

user := g.Group("/user")
{
  user.POST("", service.CreateUser)
  user.GET("/:username", service.GetUser)
  user.DELETE("/:id", service.DeleteUser)
  user.PUT("/:id", service.UpdateUser)
  user.POST("/token", service.PostToken)
}

// WebSockets.
w.Handle("/websocket", service.WatchUser)
// Message handlers.
m.Capture("user", mq.MsgSendMail, service.SendMail)
// Event handlers.
e.Capture(event.EvtUserCreated, service.UserCreated)

/router 用來定義路徑,我們亦會在此定義 WebSocket、訊息佇列、事件監聽器。

Server

func TestPostUser(t *testing.T) {
	assert := assert.New(t)

	u, err := c.PostUser(&model.User{
		Username: "admin",
		Password: "testtest",
	})
	assert.True(err == nil)
	err = u.Compare("testtest")
	assert.True(err == nil)
}

/server 是服務的主要核心,用來啟動服務的地方。我們亦會在此撰寫單元測式,而這個測試方式式呼叫客戶端與我們的程式溝通,然後判斷回傳的內容是否符合預期。

Service

// GetUser gets an user by the user identifier.
func GetUser(c *gin.Context) {
	// Get the `username` from the url parameter.
	username := c.Param("username")
	// Get the user by the `username` from the database.
	u, err := store.GetUser(c, username)
	if err != nil {
		errno.Abort(errno.ErrUserNotFound, err, c)
		return
	}

	c.JSON(http.StatusOK, u)
}

/service 是服務中最重要的邏輯地帶,這裡是用來處理服務邏輯的,我們亦會在此處理接收到的事件、訊息。

// Publish the `send_mail` message to the message queue.
mq.Publish(c, mq.M{
  Topic: mq.MsgSendMail,
  Data:  &u,
})
// Send the `user_created` event to Event Store.
event.Send(c, event.E{
  Stream: event.EvtUserCreated,
  Data:   &u,
})

除了在此處理事件、訊息,我們也能夠透過上述方式廣播訊息、傳遞事件給其他服務。

Datastore

// GetUser gets an user by the user identifier.
func (db *datastore) GetUser(username string) (*model.User, error) {
	u := &model.User{}
	d := db.Where(&model.User{Username: username}).First(&u)
	return u, d.Error
}

/store 是用來存放專門存取資料庫的函式,我們亦會在此撰寫單元測試,這個測試只會測試和資料庫的存取,而不是整個服務的測試。

Version

var (
	// VersionMajor is for an API incompatible changes
	VersionMajor = 0
	// VersionMinor is for functionality in a backwards-compatible manner
	VersionMinor = 1
	// VersionPatch is for backwards-compatible bug fixes
	VersionPatch = 0
	// VersionDev indicates development branch. Releases will be empty string.
	VersionDev = "dev"
)

/version 裡面定義了這個服務的版本,這個資訊會在註冊至服務中心一併轉換成服務中心內的標籤。

還請參閱

這裡整理了一些也許能夠協助你理解微服務如何運作的文件。

正體中文

一個基於 Golang 的基本 Go kit 微服務範例

1. 什麼是微服務?——Golang 微服務實作教學與範例

2. 微服務概念與溝通——Golang 微服務實作教學與範例

3. 相關工具介紹與安裝——Golang 微服務實作教學與範例

Go Kit 範例

go-kit/stringsvc3

go-examples/nsq

原文參考

An Introduction to Microservices, Part 1

API Gateway. An Introduction to Microservices, Part 2

An Introduction to Microservices, Part 3: The Service Registry

Intro to Microservices, Part 4: Dependencies and Data Sharing

啟發

drone/drone

License

MIT © Yami Odymel

About

一個基於 Golang、Consul、Prometheus、EventStore、Gin、Gorm、NSQ 的微服務起始結構。

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors