当前位置: 首页 > 工具软件 > Go-kit > 使用案例 >

golang工程组件之微服务工具集go-kit

太叔正文
2023-12-01

前言

随着互联网的发展,系统规模越来越大,单体应用已经无法满足复杂业务需求。因此,一种新的架构模式——微服务(Microservices)应运而生。微服务架构将系统拆分成独立的、可扩展的小服务,每个服务都可独立开发、测试、部署和运行。这样可以有效降低系统耦合度,提高代码复用性和可维护性。

然而,微服务架构也带来了一系列新的挑战,如服务治理、服务发现、负载均衡、日志收集等问题。为了解决这些问题,社区涌现出了许多微服务框架和工具,其中go-kit就是其中一个比较受欢迎的微服务工具集。本文将详细介绍go-kit的相关概念、使用方法和实现原理。

go-kit概述

go-kit是一个基于Go语言的微服务工具集,它提供了一系列组件,帮助开发者构建健壮、可靠、可扩展的微服务应用。它包含了通信、服务发现、负载均衡、熔断器、日志等多个方面的功能。

go-kit提倡使用RPC(Remote Procedure Call)作为服务之间通信的方式,并采用了Transport和Endpoint模式来实现服务之间的通信。其中,Transport负责协议转换和网络传输,Endpoint负责处理业务逻辑和参数解析。

另外,go-kit还提供了一系列中间件,如Logging、Tracing、Endpoint Instrumenting等,用于增强服务的可观察性和可维护性。同时,它也支持多种协议和框架,如HTTP、gRPC、thrift等。

go-kit核心组件

Transport

Transport是go-kit中处理网络传输的组件,它提供了HTTP、gRPC、thrift等多种协议的支持。在使用Transport时,需要先定义一个Handler接口,然后根据不同协议的需求实现对应的Transport。例如,在HTTP协议下,可以使用httptransport.NewServer()方法创建一个HTTP服务端:

func main() {
    svc := myService{}
    
    // create endpoints
    helloEndpoint := makeHelloEndpoint(svc)
    
    // create http.Handler
    r := mux.NewRouter()
    r.Methods("POST").Path("/hello").Handler(httptransport.NewServer(
        helloEndpoint,
        decodeRequest,
        encodeResponse,
    ))
    
    log.Fatal(http.ListenAndServe(":8080", r))
}

上述代码中,我们首先创建了一个myService结构体,然后根据业务需求分别定义了helloEndpoint。接着,使用httptransport.NewServer()方法创建一个HTTP服务端,并将helloEndpoint、decodeRequest、encodeResponse传入其中。最后,我们使用HTTP标准库中的ListenAndServe()方法启动HTTP服务。

在上述代码中,我们还通过mux.NewRouter()方法创建了一个路由器,用于处理HTTP请求的路由。对于不同的请求路径和HTTP方法,可以使用r.Methods()和r.Path()方法进行绑定。

Endpoint

Endpoint是go-kit中处理请求的组件,它负责处理具体的业务逻辑和参数解析。在使用Endpoint时,需要先定义一个Endpoint结构体,然后根据业务需求实现对应的Endpoint方法。例如,在实现上述HTTP服务中,我们可以定义如下的helloEndpoint:

func makeHelloEndpoint(svc myService) endpoint.Endpoint {
    return func(ctx context.Context, request interface{}) (interface{}, error) {
        req := request.(helloRequest)
        resp := helloResponse{Message: svc.SayHello(req.Name)}
        return resp, nil
    }
}

上述代码中,我们首先定义了一个空的myService结构体,然后定义了helloRequest和helloResponse两个请求和响应参数结构体。最后,我们根据具体的业务需求实现了SayHello()方法,并将其绑定到myService结构体上。

3.4 Middleware

Middleware是go-kit中负责增强服务功能的组件,它可以在请求处理前后进行拦截和增强。在使用Middleware时,需要先定义一个Middleware类型,并实现对应的Middleware方法。例如,在实现日志记录功能时,可以定义如下的loggingMiddleware:

func loggingMiddleware(logger log.Logger) endpoint.Middleware {
    return func(next endpoint.Endpoint) endpoint.Endpoint {
        return func(ctx context.Context, request interface{}) (interface{}, error) {
            logger.Log("msg", "request start")
            defer logger.Log("msg", "request end")
            return next(ctx, request)
        }
    }
}

上述代码中,我们首先定义了一个loggingMiddleware()函数,该函数返回一个Middleware方法。在方法内部,我们接收一个log.Logger类型的日志记录器,并返回一个Endpoint方法。在这个Endpoint方法中,我们使用defer语句记录请求开始和结束的日志。

需要注意的是,在使用Middleware时,每个中间件都可以对请求进行处理,并将结果传递给下一个中间件。因此,在使用多个中间件时,需要注意它们之间的先后顺序。

go-kit实例

HTTP服务

下面,我们将通过一个简单的HTTP服务实例,演示如何使用go-kit构建微服务应用。

首先,我们创建一个hello-service项目,并初始化go.mod文件:

$ mkdir hello-service && cd hello-service
$ go mod init hello-service

然后,我们添加三个文件:main.go、service.go和endpoints.go。其中,main.go用于启动HTTP服务,service.go用于定义业务逻辑,endpoints.go用于定义Endpoint方法。

main.go:

package main

import (
    "context"
    "encoding/json"
    "flag"
    "log"
    "net/http"
    "os"
    "os/signal"
    "syscall"
    "time"

    "github.com/gorilla/mux"
    kitlog "github.com/go-kit/kit/log"
    httptransport "github.com/go-kit/kit/transport/http"
)

func main() {
    var (
        httpAddr = flag.String("http.addr", ":8080", "HTTP listen address")
    )

    logger := kitlog.NewLogfmtLogger(os.Stderr)
    logger = kitlog.With(logger, "ts", kitlog.DefaultTimestampUTC)

    svc := helloService{}

    helloHandler := httptransport.NewServer(
        makeHelloEndpoint(svc),
        decodeHelloRequest,
        encodeResponse,
    )

    r := mux.NewRouter()
    r.Handle("/hello", helloHandler).Methods("POST")

    srv := &http.Server{
        Handler:      r,
        Addr:         *httpAddr,
        WriteTimeout: 5 * time.Second,
        ReadTimeout:  5 * time.Second,
    }

    go func() {
        if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
            logger.Log("err", err)
        }
    }()

    c := make(chan os.Signal, 1)
    signal.Notify(c, syscall.SIGINT, syscall.SIGTERM)
    <-c

    ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
    defer cancel()

    if err := srv.Shutdown(ctx); err != nil {
        logger.Log("err", err)
    }
}

type response struct {
    Error string `json:"error,omitempty"`
    Message string `json:"message,omitempty"`
}

func encodeResponse(_ context.Context, w http.ResponseWriter, response interface{}) error {
    return json.NewEncoder(w).Encode(response)
}

func decodeHelloRequest(_ context.Context, r *http.Request) (interface{}, error) {
    var req struct {
        Name string `json:"name"`
    }
    if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
        return nil, err
    }
    return req, nil
}

service.go:

package main

type helloService struct{}

func (helloService) SayHello(name string) string {
    return "Hello, " + name + "!"
}

endpoints.go:

package main

import (
    "context"

    "github.com/go-kit/kit/endpoint"
)

func makeHelloEndpoint(svc helloService) endpoint.Endpoint {
    return func(ctx context.Context, request interface{}) (interface{}, error) {
        req := request.(struct{ Name string })
        return response{Message: svc.SayHello(req.Name)}, nil
    }
}

在上述代码中,我们首先定义了一个helloService结构体,并实现了SayHello()方法。然后,根据业务逻辑,我们分别定义了makeHelloEndpoint()、decodeHelloRequest()和encodeResponse()三个函数。其中,makeHelloEndpoint()用于创建Endpoint方法,decodeHelloRequest()用于解析HTTP请求参数,encodeResponse()用于编码HTTP响应结果。

最后,我们使用http.ListenAndServe()方法启动HTTP服务,并添加了优雅关闭的逻辑。在这个例子中,我们使用了gorilla/mux库来处理HTTP请求路由。

gRPC服务

除了HTTP协议,go-kit还支持gRPC协议。下面,我们将演示如何使用go-kit构建gRPC服务。

首先,我们创建一个grpc-service项目,并初始化go.mod文件:

$ mkdir grpc-service && cd grpc-service
$ go mod init grpc-service

然后,我们添加三个文件:main.go、service.go和endpoints.go。其中,main.go用于启动gRPC服务,service.go用于定义业务逻辑,endpoints.go用于定义Endpoint方法。

main.go:

package main

import (
    "context"
    "flag"
    "fmt"
    "log"
    "net"
    "os"
    "os/signal"
    "syscall"

    "google.golang.org/grpc"
)

func main() {
    var (
        grpcAddr = flag.String("grpc.addr", ":8080", "gRPC listen address")
    )

    logger := log.New(os.Stderr, "", log.LstdFlags)

    svc := helloService{}

    helloServer := newHelloServer(makeHelloEndpoint(svc))

    lis, err := net.Listen("tcp", *grpcAddr)
    if err != nil {
        logger.Fatalf("failed to listen: %v", err)
    }

    s := grpc.NewServer()
    RegisterHelloServer(s, helloServer)

    go func() {
        logger.Printf("starting gRPC server at %s\n", *grpcAddr)
        if err := s.Serve(lis); err != nil {
            logger.Fatalf("failed to serve: %v", err)
        }
    }()

    c := make(chan os.Signal, 1)
    signal.Notify(c, syscall.SIGINT, syscall.SIGTERM)
    <-c

    logger.Println("shutting down gRPC server")

    s.GracefulStop()

    logger.Println("gRPC server stopped")
}

type helloServer struct {
    hello HelloServer
}

func newHelloServer(hello Endpoint) *helloServer {
    return &helloServer{
        hello: hello,
    }
}

func (s *helloServer) SayHello(ctx context.Context, req *HelloRequest) (*HelloResponse, error) {
    _, resp, err := s.hello(ctx, helloRequest{Name: req.Name})
    if err != nil {
        return nil, err
    }
    return resp.(*HelloResponse), nil
}

service.go:

package main

type helloService struct{}

func (helloService) SayHello(name string) string {
    return "Hello, " + name + "!"
}

endpoints.go:

package main

import (
    "context"

    "github.com/go-kit/kit/endpoint"
)

func makeHelloEndpoint(svc helloService) endpoint.Endpoint {
    return func(ctx context.Context, request interface{}) (interface{}, error) {
        req := request.(helloRequest)
        return helloResponse{Message: svc.SayHello(req.Name)}, nil
    }
}

在上述代码中,我们首先定义了一个helloService结构体,并实现了SayHello()方法。然后,根据业务逻辑,我们分别定义了makeHelloEndpoint()和newHelloServer()两个函数。其中,makeHelloEndpoint()用于创建Endpoint方法,newHelloServer()用于创建gRPC服务端对象。

最后,我们使用grpc.NewServer()方法创建gRPC服务端,使用RegisterHelloServer()方法注册服务到gRPC服务器,并启动服务。需要注意的是,在这个例子中,我们使用了Google提供的proto工具来生成gRPC相关的代码。

总结

本文介绍了go-kit这个微服务工具集的概念、使用方法和实现原理。通过一个HTTP服务和一个gRPC服务的实例,我们演示了如何使用go-kit构建微服务应用,并解释了Transport、Endpoint、Service和Middleware等核心组件的作用。

相比其他微服务框架和工具,go-kit具有轻量、灵活和可扩展的特点。它提供了丰富的组件和中间件,可以满足不同的业务需求。因此,在开发微服务应用时,可以考虑使用go-kit这个工具集,以提高系统的可靠性和可维护性。

 类似资料: