golang:go-kit微服务

简介

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

  • 它是框架的底层,我们可以用go-kit 做适应自己平台的框架。它的设计目标是帮助开发者构建健壮、可维护、可测试的分布式系统。

  • go-kit 的核心理念是通过可组合的组件来实现微服务的功能,这些组件包括服务发现、负载均衡、请求追踪、日志记录、监控等等。go-kit还提供了一种基于中间件的设计模式,使开发人员能够构建可插拔的组件,从而更容易地构建和维护可扩展的分布式应用程序。

  • go-kit 的设计目标是简单、灵活、可扩展。它提供了一些基本的组件,如服务端点(endpoint)、传输层(transport)、服务发现、负载均衡等,这些组件可以通过接口进行组合,并且可以根据实际需求进行扩展和定制。

  • go-kit采用了微服务架构的理念,将应用程序分解为一组小型服务,每个服务都具有单独的职责。每个服务都是独立的,可以独立部署、升级和扩展。通过这种方式,go-kit可以帮助开发人员更容易地构建可扩展的应用程序。
    在这里插入图片描述

官网 - https://gokit.io/

Github - https://github.com/go-kit/kit

什么是微服务

go-kit是为了更方便的实现微服务而存在的。

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

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

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

如何高效的使用go-kit

前面有提到,go-kit本身分为三层,针对这点有同学会提出:“每次新建项目,都需要手动写下go-kit的这三层逻辑,有点浪费时间,不够简洁”,这确实是一个共性问题,从go-kit的github的issue中可以发现,也有不少人反馈过类似问题。很庆幸的是,有人给我们铺好了路,详见GoKit CLi,其主要功能如图5所示。

在这里插入图片描述
这个工具可以根据我们的需求自动生成service,transport和endpoint模版,以及生成供调用方的使用的client library,节省我们大量的时间,提高我们的生产效率。具体操作步骤,可以参考GoKit CLi的说明

go-kit三层结构

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

Transport

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

这一层具有很多通信协议相关的功能。比如在HTTP协议中,根据返回数据的具体格式,在HTTP头中设置Content-Type。这部分的功能具有很强的重复性,如果设计良好,往往不需要太多的coding。

Endpoint

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

  • 从一个请求来说,通过Transport上一定的路由关系后,就会落到具体的Endpoint层。一个具体的Endpoint有两个关键的、类型确定的参数:请求与响应。
  • 定义中还讲到了安全与反脆弱性,可以理解为在Endpoint层要处理panic等异常信息,不要让Endpoint与Service层的问题影响到整个服务的稳定性。

Endpoint 是 go-kit 中最重要的组件之一,它用于封装服务操作的输入和输出。通过 Endpoint,开发者可以将一个服务拆分成多个小的可复用的操作,这些操作可以独立地进行测试、扩展和部署。

在这个定义的类型里面会去调用service层的方法,组装成response返回。而gokit中的所有中间件组件都是通过装饰者设计模式注入的。

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

func(log Logger, in endpoint.Endpoint) endpoint.Endpoint {
    return func(ctx context.Context, req interface{}) (interface{}, error) {
            logger.Log("input", toJSON(req))
            resp, err := in(ctx, req)
            logger.Log("output", toJSON(resp), "err", err)
            return resp, err
    }
}

Service

服务(指Go kit中的service层)是实现所有业务逻辑的地方。服务层通常将多个端点粘合在一起。在 Go kit 中,服务层通常被抽象为接口,这些接口的实现包含业务逻辑。Go kit 服务层应该努力遵守整洁架构或六边形架构。也就是说,业务逻辑不需要了解端点(尤其是传输域)概念:你的服务层不应该关心HTTP 头或 gRPC 错误代码。

                +-----------+
    Request -->| Transport |--> Endpoint --> Service
                +-----------+
                    ^
                    |
    Response <------+

// 求从 Transport 组件进入,然后被传递到 Endpoint 组件。Endpoint 组件封装了服务操作的输入和输出,通过调用 Service 组件来处理具体的业务逻辑,并将结果返回给 Endpoint 组件。最后,Endpoint 组件将结果返回给 Transport 组件,由 Transport 组件发送回客户端。

Middlewares

go-kit除了经典的分层架构外,还在endpoint层提供了很多公用的拦截器,如log,metric,tracing,circuitbreaker,rate-limiter等,来保障业务系统的可用性。它们在设计上有一个共同特点:都是同传输协议无关的。

Go kit 试图通过使用中间件(或装饰器)模式来执行严格的关注分离(separation of concerns)。中间件可以包装端点或服务以添加功能,比如日志记录、速率限制、负载平衡或分布式跟踪。围绕一个端点或服务链接多个中间件是很常见的。

在这里插入图片描述

其他

将所有这些概念放在一起,我们可以看到 Go kit 构建的微服务就像洋葱一样有许多层。
在这里插入图片描述
这些层可以分组到我们的三个域中:

  • 最内层的服务(service)域是所有内容都基于特定服务定义的地方,也是实现所有业务逻辑的地方。
  • 中间端点(endpoint)域是将服务的每个方法抽象为通用 endpoint.Endpoint以及实现安全性和抗脆弱性逻辑的位置。
  • 最外层的传输(transport)域是端点绑定到 HTTP 或 gRPC 等具体传输的地方。

你可以通过为服务定义接口并提供具体的实现来实现核心业务逻辑。然后,编写服务中间件来提供额外的功能,比如日志记录、分析、检测ーー任何需要了解业务领域知识的东西。

Go kit 提供端点和传输域中间件,用于诸如速率限制、断路、负载平衡和分布式跟踪等功能ーー所有这些功能通常与你的业务域无关。

简而言之,Go kit 试图通过精心使用中间件(或装饰器)模式来强制执行严格的关注分离(separation of concerns)。

快速开始

D:.
│  go.mod
│  main.go
│
├─endpoint
│      hello_end_point.go
│
├─service
│      hello_server.go
│
└─transport
        hello_transport.go

go-kit构建微服务步骤

  • 定义业务逻辑和数据模型:定义服务接口和数据模型,包括请求和响应数据结构。
  • 实现服务:根据定义的接口和数据模型实现服务,包括服务接口和业务逻辑实现。
  • 创建 endpoint:创建 endpoint 对象,将业务逻辑封装成 endpoint。
  • 创建 transport:创建 transport 对象,处理网络传输,包括编码、解码和传输数据。
  • 创建服务实例:创建服务实例,将 endpoint 和 transport 组装起来,创建服务实例。
  • 创建中间件:创建中间件对象,实现一些通用的逻辑,例如认证、授权、限流、熔断、追踪、日志等。
  • 组装中间件和服务实例:将中间件对象和服务实例组装起来,形成一个完整的服务实例。

hello_server.go

service 层,提供具体的业务实现接口,endpoint 层中的 Endpoint 通过调用 service 层的接口方法处理请求。

package service

import "fmt"

//用于定义业务方法的接口
type IHelloService interface {
	Hello(name string) string
}

//用于实现上面定义的接口
type HelloService struct {
	//根据业务需求填充结构体
}

//实现上方定义的业务方法
func (h HelloService) Hello(name string) string {
	return fmt.Sprintf("Hello:%s", name)
}

hello_end_point.go

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

package endpoint

import (
	"context"
	"github.com/go-kit/kit/endpoint"
	"github.com/mwqnice/go-kit-demo/service"
)
// Hello 业务使用的请求和响应格式
//HelloRequest 请求格式
type HelloRequest struct {
	Name string `json:"name"`
}

//HelloResponse响应格式
type HelloResponse struct {
	Reply string `json:"reply"`
}

//创建构造函数hello方法的业务处理
// MakeHelloServiceEndPorint 创建关于业务的构造函数
// 传入 Service/HelloService.go 定义的相关业务接口
// 返回 go-kit/endpoint.Endpoint (实际上就是一个函数签名)
func MakeHelloServiceEndPorint(s service.IHelloService) endpoint.Endpoint {
	// 这里使用闭包,可以在这里做一些中间件业务的处理
	return func(ctx context.Context, request interface{}) (response interface{}, err error){
		// request 是在对应请求来时传入的参数(这里的request 实际上是等下我们要将的Transport中一个decode函数中处理获得的参数)
		// 这里进行以下断言
		r, ok := request.(HelloRequest)
		if !ok {
			return HelloResponse{}, nil
		}
		// 这里实际上就是调用我们在Service/HelloService.go中定义的业务逻辑
		// 我们拿到了 Request.Name 那么我们就可以调用我们的业务 service.IHelloService 中的方法来处理这个数据并返回
		// 具体的业务逻辑具体定义....
		return HelloResponse{Reply: s.Hello(r.Name)}, nil
		// response 这里返回的response 可以返回任意的 不过根据规范是要返回我们刚才定义好的返回对象
	}
}

对于每一个服务接口,endpoint 层都使用一个抽象的 Endpoint 来表示 ,我们可以为每一个 Endpoint 装饰 Go-kit 提供的附加功能,如日志记录、限流、熔断等。

hello_transport.go

指定项目提供服务的方式,比如 HTTP 或者 gRPC 等

package transport

import (
	"context"
	"encoding/json"
	"errors"
	"github.com/mwqnice/go-kit-demo/endpoint"
	"net/http"
)
// Transport/transport.go 主要负责HTTP、gRpc、thrift等相关的逻辑

// 这里有两个关键函数
// DecodeRequest & EncodeResponse 函数签名是固定的哟
// func DecodeRequest(c context.Context, request *http.Request) (interface{}, error)
// func EncodeResponse(c context.Context, w http.ResponseWriter, response interface{}) error

// HelloDecodeRequest 解码 后封装至 EndPoint中定义的 Request格式中
func HelloDecodeRequest(c context.Context, request *http.Request) (interface{}, error) {
	// 这里主要就是通过 request 拿到对应的参数构造成在 EndPoint中定义的 Request结构体即可

	name := request.URL.Query().Get("name")
	if name == "" {
		return nil, errors.New("参数name不能为空")
	}
	return endpoint.HelloRequest{Name: name}, nil
}

//HelloEncodeResponse通过响应封装成Endpoint中定义的Response结构体
func HelloEncodeResponse(c context.Context, w http.ResponseWriter, response interface{}) error {
	//这里将Response返回成有效的json格式给http

	//设置请求头信息
	w.Header().Set("Content-Type", "application/json;charset=utf-8")

	//使用内置json包转换
	return json.NewEncoder(w).Encode(response)
}




main.go

package main

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

// 服务发布
func main()  {
	//1.创建我们最开始定义的service/HelloService.go
	s := service.HelloService{}

	//2.在用endpoint/HelloEndpoint创建业务服务
	hello := endpoint.MakeHelloServiceEndPorint(s)

	//3.使用kit创建handler
	//传入业务服务 以及 定义的加密解密方法
	helloServer := httpTransport.NewServer(hello, transport.HelloDecodeRequest,transport.HelloEncodeResponse)

	// 4.使用http包启动服务
	go http.ListenAndServe("0.0.0.0:8000", helloServer)
	select {}
}

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

效果

在这里插入图片描述

第二个例子

其他类似例子

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值