[Go-Kit 001] Say hello to Go-Kit!

Kritchayan Plianpheng
Touch Technologies
Published in
3 min readJun 5, 2019
Go-Kit!

หลายคนอาจจะมีความต้องการในการสร้าง Microservices แต่ไม่รู้จะใช้อะไรทำ เราขอแนะนำ Go Language และ Framework Go Kit หรือใครไม่รู้ว่าคืออะไรเรามาทำความรู้จักมันกันเถอะ

# Go Kit คือ …

มันก็คือ Framework ที่ใช้สร้าง Microservices ที่มีความมุ่งเน้นทางด้าน Business Logic โดยใช้จัดการ services ข้อมูลในด้านต่าง ๆ และ ยังมุ่งเน้นต่อการสร้างความน่าเชื่อถือให้แก่องค์กร อีกด้วย ทั้งนี้ทั้งนั้นไม่พูดมาก ขนาด Facebook, Twitter, Netflix และ SoundCloud ยังใช้เลย และอีกอย่างคือ มันหลีกเลี่ยงปัญหาความเข้ากันได้ของ Package อีกด้วย

เราเกริ่นกันมามากละ มาลองทำดูละกัน โดยใช้ต้นแบบจาก StringSvc1 ใน Git ของ Go-Kit นะ

เริ่มจากการสร้าง Interface อารมณ์ คือการกำหนดฟังก์ชั่นต่าง ๆ คล้ายๆสร้าง Function ใน C หรือ C++ โดยผมสร้างเครื่องคณิตเลขบวกลบง่ายๆละกัน

  • เริ่ม Business Logic ก่อนเลย
type Calculator interface {
Add(a int, b int) int
Minus(a int, b int) int
}
  • ต่อมาก็ Implement มัน! ก็จะได้ฟังก์ชั่นบวกลบง่ายๆสักอัน
type calculatorService struct {}func (calculatorService) Add(a int, b int) int {
return a + b
}
func (calculatorService) Minus(a int, b int) int {
return a - b
}
  • ต่อมาคือ Requests and responses เลย นี้ อันสุดท้ายของส่วน Inferface และ

เรารับค่าเป็น a และ b ในรูปแบบ Json และส่งออกเป็น result ในรูปแบบ Json เช่นกัน!

type calculatorRequest struct {
A int `json:"a"`
B int `json:"b"`
}
type calculatorResponse struct {
R int `json:"result"`
}

หลังจากสร้าง โค้ดส่วน Interface เสร็จ, เราก็ทำ End-Point ก่อนเลย ซึ่ง Go-Kit มีไว้ให้อยู่แล้วเนอะ อยู่ที่ github.com/go-kit/kit/endpoint การทำงานของมันคือรับและส่งข้อมูลเป็น JSON เลย แต่มีการเข้าและถอดรหัสด้วย เราสามารถใช้ที่เขาให้มาได้เลย

func makePlusEndpoint(csvc Calculator) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
req := request.(calculatorRequest)
result := csvc.Add(req.A, req.B)
return calculatorResponse{result}, nil
}
}

func makeMinusEndpoint(csvc Calculator) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
req := request.(calculatorRequest)
result := csvc.Minus(req.A, req.B)
return calculatorResponse{result}, nil
}
}

พอเสร็จ Endpoint เราก็มาสร้างส่วนของ Main หรือ ส่วนเรียกใช้งาน Microservices เราจะใช้ส่วนของ httptransport ของ Go-Kit ซึ่งโดยผมจะเซ็ต Port Microservices ไว้ที่ 9000 บ้างตัว และ ผมใช้ ฟังก์ชั่นเดียวกันนะ

  • อันนี้คือ ส่วน Transports หรือ Main ของเรานั้นเอง บวก ฟังชั่นเข้าและถอดรหัส
func main() {
calService := &calculatorService{}

addFuncHandler := httptransport.NewServer(
makePlusEndpoint(calService),
decodeRequest,
encodeResponse,
)
minusFuncHandler := httptransport.NewServer(
makeMinusEndpoint(calService),
decodeRequest,
encodeResponse,
)

http.Handle("/plus", addFuncHandler)
http.Handle("/minus", minusFuncHandler)

log.Fatal(http.ListenAndServe(":9000", nil))
}
func decodeRequest(_ context.Context, r *http.Request) (interface{}, error) {
var req calculatorRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
return nil, err
}
return req, nil
}

func encodeResponse(_ context.Context, w http.ResponseWriter, response interface{}) error {
return json.NewEncoder(w).Encode(response)
}

หลังจากเขียนเสร็จหมดแล้ว มาลองทดสอบด้วยการยิง CURL ผ่าน CMD กันดีกว่า โดยผมจะใช้คำสั่งตามนี้

curl -X POST -d "{\"a\":2, \"b\":3}" http://127.0.0.1:9000/plus
ผลลัพธ์ที่ได้คือ {“result”:5}

มาลอง ฟังก์ชั่นลบกันบ้าง

curl -X POST -d "{\"a\":2, \"b\":6}" "http://127.0.0.1:9000/minus"
ผลลัพธ์ที่ได้คือ {"result":-4}

แต่เราจะลองอะไรแปลกๆกัน มันไร้สาระไม่ต้องอ่านก็ได้ ลองแบบส่งค่าตัว แปรเป็นตัวอักษรบ้าง

curl -X POST -d "{\"a\":2, \"b\":\"A\"}" http://127.0.0.1:9000/minus
ผลลัพธ์ที่ได้คือ json: cannot unmarshal string into Go struct field calculatorRequest.b of type int

แต่จริง ๆ เราคงรู้ผลลัพธ์อยู่แล้ว เรากำลังเป็น Int ไว้ยกเว้น ว่างทำตัวตรวจเช็คการส่งค่าเข้ามาอะเน้ออออ

# เผื่อไม่เห็นภาพ :)

เสริมอีกเล็กน้อย ส่วน สุดท้าย Middlewares อารมณ์เหมือนเอาไรไปคั้นกลาง เช่น

  • Serialization
  • Rate Limit
  • Logging
  • Request tracing
  • Circuit Breaker

- อื่นๆดูได้ที่ Git ของ Go-Kit เลย :)

มันเป็นตัวคล้ายตัวคั่นระหว่างอะนะ ยกตัวอย่าง Rate Limit ก็ ถ้า Request ข้อมูลครั้งนึง เรา ตั้งค่าว่าอีก 1 นาที ถึง Request ได้ใหม่ ถ้า Request ก่อนก็แจ้ง Error ประมาณนี้ สามารถ ลองดูตัวอย่างพวก Logging ได้ที่นี้ อยู่ล่างๆหน่อย หรือ ค้นหา Middlewares

https://gokit.io/examples/stringsvc.html

และสุดท้าย คุณสามารถศึกษาตัวอย่างอื่น ๆต่างได้จาก

https://github.com/go-kit/kit/tree/master/examples

ขอแถมอีกหน่อยละกันนน เรื่องการวางโพสเดอร์แบบ DDD Folder Structure

หลายคนอาจจะไม่เคยได้ยินมันคืออะไร แต่จริงมันคือการจัดวางโพสเดอร์เพื่อให้ง่ายต่อการแก้ไขและทำให้เรารู้ที่อยู่ได้อย่างง่ายดายนั้นละ ไม่มีไรมาก ขอยกตัวอย่างเลยละกัน เป็นอารมณ์เหมือนเราสร้างบ้านต้องมีอะไร เช่น มีบ้าน ต้องมี อุปกรณ์สร้างบ้านอะ

golang folder/
├── businesslogic/
│ ├── interface.go
│ ├── implement.go
│ ├── struct.go
├── endpoint/
│ ├── add.go
│ ├── minus.go
├── middlewares/
│ ├── ratelimit.go
└── main.go

ที่อ่านมาถึงตรงนี้เนอะ คนเขียนก็ไม่รู้ว่าถูกต้องทั้งหมดหรือไม่ ผมขออภัยไว้ล่วงหน้านะ มาลองใช้กันดู เจอกันใหม่

--

--