【Golang】微服务实现工具 -- go-kit 易懂

【Golang】微服务实现工具 -- go-kit 易懂,第1张

go-kit是一个分布式的开发工具集,在大型的组织(业务)中可以用来构建微服务,其解决了分布式系统中大多数常见问题,因此,使用者可以将精力集中在业务逻辑上

首先我们要明白,go-kit不是一个框架,他只是一个工具集,他里面有用来帮助我们实现微服务的一些工具包,所以他并不想SpringBoot那样能帮我们直接把框架搭好,我们只要在项目框架上直接写我们的代码就好了。

但是他跟SpringBoot又有相似的地方,比如MVC和go-kit的三层架构。

微服务

要了解go-kit我们首先要知道什么事微服务,毕竟go-kit是为了更方便的实现微服务而存在的。

维基百科:

微服务(英语:Microservices)是一种软件架构风格,它是以专注于单一责任与功能的小型功能区块 (Small BuildingBlocks) 为基础,利用模块化的方式组合出复杂的大型应用程序,各功能区块使用与语言无关(Language-Independent/Language agnostic)的API集相互通信。

我们可以知道,微服务就是一个大项目中的一个小功能块,并且这个功能块可以不计语言差别的和其他功能模块通过API进行通讯和结合。

所以通过微服务,我们可以知道,工作之间的接触就可以完全是,这个模块我要输入什么得到什么输出,有另外一个人那里沟通好,其他的就不用管了,让他一个人安安静静的开发就好了,等他做完我按照这个格式去取我想要的结果就好了。

go-kit三层结构

go-kit和SSM中的MVC一样也有三层结构endpoint,service, transport,通过这三层结构来实现,接收一个请求,然后返回一个结果。

Transport
Transport处于该微服务的最上层,主要负责于HTTP, gRPC,thrift等相关的逻辑,负责解析请求,并且调用endpoint来处理请求

Endpoint
endpoint属于客户端和服务端的转接器,他定义了request和response的格式,上层给Transport调用来处理请求,下层链接Service将Service所返回的结果转换成request需要的格式

Service
最底下的我们这个模块所需要实现的功能了

下面我们通过一个简单的例子来详细了解一下。我们得到的需求是,传入一个用户id,然后返回该id对应的姓名。

go-kit实例

首先创建我们的基本框架,因为是入门级项目,我们就严格按照go-kit的三层结构来设计我们的框架。

Service

最简单的,我们先实现我们最基本的需求解决方法吧,这里最重要的就是GetName接口以及他的实现,这一层比较简单

package service

type IUserService interface {
	GetName(userId int) string
}

type UserService struct {
}

func (receiver UserService) GetName(userId int) string  {
	if userId == 101 {
		return "calvin"
	}
	return "guest"
}

EndPoint

endpoint的作用最简单的,对于一个从上层Transport传入的request,我们返回一个response,也就是可以写成这样

// 这定义了一个类似接口的方法Endpoint
type EndPoint func(request) response 

写得再详细一点就是

type EndPoint func(ctx context.Context, request interface{}) (response interface{}, err error)

// 所以endpoint主要的功能其实主要是返回一个函数,但是对于我们来说,要将底层的Service封装进这个函数中去,所以应该更详细的写成
func GetEndPoint(service Service) endpoint.Endpoint{
	return func(ctx context.Context, request interface{})(response interface{}, err error){
		// 实现算法
	}
}

其实context和reques interface以及response interface不了解的不重要,我们现在知道这是固定的格式就好了,以后有更深入的需要和了解再去学习。

大概了解了endpoint的格式之后我们就来写自己的endpoint的方法

package endpoint

import (
	"context"
	"github.com/go-kit/kit/endpoint"
	"go-kit-test/service"
)
// 定义request输入的格式 uid:xx json格式
type UserRequest struct{
	Uid int `json: "uid"`
}
// 定义response输出格式 result:xx json格式
type UserResponse struct{
	Result string `json: "result" `
}
// interface{} 的意思是request和response输入和输出无论什么格式都可以
func GetUserEndPonint(userService service.IUserService) endpoint.Endpoint{
	return func(ctx context.Context, request interface{})(response interface{},err error){
		r:= request.(UserRequest)
		result := userService.GetName(r.Uid)
		return UserResponse{Result: result}, nil
	}
}

上面的代码大多数都比较好理解,真正处理的部分,r:=request.(UserRequest)我们都知道,拿到request中的信息,可以通过r.Uid得到我们最底层service需要得到的输入,但是这个函数是怎么实现的呢,下面就需要Transport层了,毕竟一个request不会无缘无故形成endpoint需要的格式

Transport
package transport

import (
	"context"
	"encoding/json"
	"errors"
	"go-kit-test/endpoint"
	"net/http"
	"strconv"
)

//这一块为核心实现,从一个http请求,request得到url,然后通过解析url得到我们所需要的uid,然后将其转化成endpoint中定义好的UserRequest模式返回
func DecodeUserRequest(ctx context.Context, r *http.Request) (interface{}, error) {
	if r.URL.Query().Get("uid") != "" {
		uid, _ := strconv.Atoi(r.URL.Query().Get("uid"))
		return endpoint.UserRequest{Uid: uid}, nil
	}
	return nil, errors.New("参数错误")
}

//这个可以暂时不用管
func EncodeUserResponse(ctx context.Context, w http.ResponseWriter, response interface{}) error {
	// transport body to json
	w.Header().Set("Content-type", "application/json")
	return json.NewEncoder(w).Encode(response)
}

可能看到这里会有些迷茫了,虽然返回了endpoint.UserRequest格式,但是这只是个函数,也没有将参数传给endpointa,那我们上面的r:=request.(UserRequest)是怎么实现的呢。

其实最重要的,我们需要建一个main方法将endpoint+service这一部分,和transport组装起来

package main

import (
	httpTransport "github.com/go-kit/kit/transport/http"
	"go-kit-test/endpoint"
	"go-kit-test/service"
	"go-kit-test/transport"
	"net/http"
)

func main() {
	user := service.UserService{}
	endPoint := endpoint.GetUserEndPoint(user)
	// implement http.handler and encapsulate with endpoint
	serverHandler := httpTransport.NewServer(endPoint, transport.DecodeUserRequest, transport.EncodeUserResponse)
	// listen the port
	_ = http.ListenAndServe(":8080", serverHandler)
}

最重要的其实就在组装这个handler的地方serverHandler := httpTransport.NewServer(endPoint, transport.DecodeUserRequest, transport.EncodeUserResponse)这里我们将DecodeUserRequestendPoint一起传入进去,在NewServer内部会有自动将结果传入request的方法,然后打开ListenAndServer函数就可以开始监听8080窗口了默认是127.0.0.1:8080


如果程序运行后我们访问http://127.0.0.1:8080?uid=101就能得到这样的结果,结果格式其实也是我们定义好的

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/996070.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-21
下一篇 2022-05-21

发表评论

登录后才能评论

评论列表(0条)

保存