手把手带你使用 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)
}
Songzhibin
分类:
GoGoGo
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 一个奇形怪状的面试题:Bean中的CHM要不要加volatile?
· [.NET]调用本地 Deepseek 模型
· 一个费力不讨好的项目,让我损失了近一半的绩效!
· .NET Core 托管堆内存泄露/CPU异常的常见思路
· PostgreSQL 和 SQL Server 在统计信息维护中的关键差异
· DeepSeek “源神”启动!「GitHub 热点速览」
· 我与微信审核的“相爱相杀”看个人小程序副业
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 如何使用 Uni-app 实现视频聊天(源码,支持安卓、iOS)
· C# 集成 DeepSeek 模型实现 AI 私有化(本地部署与 API 调用教程)
2019-11-06 常见的图片格式的区别
2019-11-06 前端基础-CSS