手把手带你使用 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)
}
posted @ 2020-11-06 11:49  Binb  阅读(1411)  评论(0编辑  收藏  举报