我們客戶端的架構與服務的類似
根據我們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)
}