手把手帶你使用 go-kit(客戶端直連)


我們客戶端的架構與服務的類似

根據我們Demo的示例,我們創建一個客戶端,客戶端與服務端架構類似

// 項目結構
-| Client
----| Client.go
-| EndPoint
----| endpoint.go
-| Transport
----| Transport.go
- main.go

1.首先我們還是先寫Client實例

// Client/client.go
package Client

import (
	"context"
	"fmt"
	"github.com/go-kit/kit/transport/http"
	"net/url"
	"strings"
)

// Direct: 直接調用服務端
// method:方法 fullUrl: 完整的url http://localhost:8000
// enc: http.EncodeRequestFunc dec: http.DecodeResponseFunc 這兩個函數具體等一下會在Transport中進行詳細解釋
// requestStruct: 根據EndPoint定義的request結構體傳參
func Direct(method string, fullUrl string, enc http.EncodeRequestFunc, dec http.DecodeResponseFunc, requestStruct interface{}) (interface{}, error) {
	// 1.解析url
	target, err := url.Parse(fullUrl)
	if err != nil {
		fmt.Println(err)
		return nil, err
	}
	// kit調用服務端拿到Client對象
	client := http.NewClient(strings.ToUpper(method), target, enc, dec)
	// 調用服務 client.Endpoint()返回一個可執行函數 傳入context 和 請求數據結構體
	return client.Endpoint()(context.Background(), requestStruct)
}

2.EndPoint與之前沒有變化,刪除了一些邏輯

// EndPoint/endpoint.go
package EndPoint

// endpoint.go 定義 Request、Response 格式, 並且可以使用閉包來實現各種中間件的嵌套
// 這里了解 protobuf 的比較好理解點
// 就是聲明 接收數據和響應數據的結構體 並通過構造函數創建 在創建的過程當然可以使用閉包來進行一些你想要的操作啦

// 這里根據我們Demo來創建一個響應和請求
// 當然你想怎么創建怎么創建 也可以共用 這里我分開寫 便於大家看的清楚

// Hello 業務使用的請求和響應格式
// HelloRequest 請求格式
type HelloRequest struct {
	Name string `json:"name"`
}

// HelloResponse 響應格式
type HelloResponse struct {
	Reply string `json:"reply"`
}

// Bye 業務使用的請求和響應格式
// ByeRequest 請求格式
type ByeRequest struct {
	Name string `json:"name"`
}

// ByeResponse 響應格式
type ByeResponse struct {
	Reply string `json:"reply"`
}

// ------------ 當然 也可以通用的寫 ----------
// Request 請求格式
type Request struct {
	Name string `json:"name"`
}

// Response 響應格式
type Response struct {
	Reply string `json:"reply"`
}

3.修改Transport內容,邏輯與服務的正好相反

// Transport/transport.go
package Transport

import (
	"Songzhibin/go-kit-demo/v0client/EndPoint"
	"context"
	"encoding/json"
	"errors"
	"net/http"
	"net/url"
	"strconv"
)

// Transport/transport.go 主要負責HTTP、gRpc、thrift等相關的邏輯

// 這里有兩個關鍵函數
// DecodeRequest & EncodeResponse 函數簽名是固定的喲
// func EncodeRequestFunc (context.Context, *http.Request, interface{}) error
// func DecodeResponseFunc (context.Context, *http.Response) (response interface{}, err error)

// HelloEncodeRequestFunc: 處理請求數據符合服務方要求的數據
func HelloEncodeRequestFunc(c context.Context, request *http.Request, r interface{}) error {
	// r就是我們在EndPoint中定義的請求響應對象
	req, ok := r.(EndPoint.HelloRequest)
	if !ok {
		return errors.New("斷言失敗")
	}
	// 拿到自定義的請求對象對url做業務處理
	request.URL.Path += "/hello"
	data := url.Values{}
	data.Set("name", req.Name)
	request.URL.RawQuery = data.Encode()
	// 實際上這里做的就是增加url參數 body之類的一些事情,簡而言之就是構建http請求需要的一些資源
	return nil
}

// HelloDecodeResponseFunc: 解密服務方傳回的數據
func HelloDecodeResponseFunc(c context.Context, res *http.Response) (response interface{}, err error) {
	// 判斷響應
	if res.StatusCode != 200 {
		return nil, errors.New("異常的響應碼" + strconv.Itoa(res.StatusCode))
	}
	// body中的內容需要我們解析成我們通用定義好的內容
	var r EndPoint.HelloResponse
	err = json.NewDecoder(res.Body).Decode(&r)
	if err != nil {
		return nil, err
	}
	return r, nil
}

// ByeEncodeRequestFunc: 處理請求數據符合服務方要求的數據
func ByeEncodeRequestFunc(c context.Context, request *http.Request, r interface{}) error {
	// r就是我們在EndPoint中定義的請求響應對象
	req, ok := r.(EndPoint.HelloRequest)
	if !ok {
		return errors.New("斷言失敗")
	}
	// 拿到自定義的請求對象對url做業務處理
	request.URL.Path += "/bye"
	data := url.Values{}
	data.Set("name", req.Name)
	request.URL.RawQuery = data.Encode()
	// 實際上這里做的就是增加url參數 body之類的一些事情,簡而言之就是構建http請求需要的一些資源
	return nil
}

// ByeDecodeResponseFunc: 解密服務方傳回的數據
func ByeDecodeResponseFunc(c context.Context, res *http.Response) (response interface{}, err error) {
	// 判斷響應
	if res.StatusCode != 200 {
		return nil, errors.New("異常的響應碼" + strconv.Itoa(res.StatusCode))
	}
	// body中的內容需要我們解析成我們通用定義好的內容
	var r EndPoint.HelloResponse
	err = json.NewDecoder(res.Body).Decode(&r)
	if err != nil {
		return nil, err
	}
	return r, nil
}

直接調用

// main.go
package main

import (
	"Songzhibin/go-kit-demo/v0client/Client"
	"Songzhibin/go-kit-demo/v0client/EndPoint"
	"Songzhibin/go-kit-demo/v0client/Transport"
	"fmt"
)

// 調用我們在client封裝的函數就好了
func main() {
	i, err := Client.Direct("GET", "http://127.0.0.1:8000", Transport.HelloEncodeRequestFunc, Transport.HelloDecodeResponseFunc, EndPoint.HelloRequest{Name: "songzhibin"})
	if err != nil {
		fmt.Println(err)
		return
	}
	res, ok := i.(EndPoint.HelloResponse)
	if !ok {
		fmt.Println("no ok")
		return
	}
	fmt.Println(res)
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM