Medium原文点这里。
对以下四个框架进行比较:
最受欢迎的框架之一,网上有很多的博客、文章,还有很多使用示例。可以在Medium上关注microhq,来获得Go Micro中的最新更新。
Go Micro是使用Go语言编写微服务的可插拔RPC框架,它开箱即用,有如下功能:
Content-Type
标头的编码/解码Go Micro架构可以描述为如下所示的三层堆栈:
顶层由客户端-服务器模型和服务抽象组成。服务器(Server)是用于编写服务的构建块,客户端(Client)提供了向服务请求的接口。
底层由以下类型的插件组成:
json
,bson
,protobuf
,msgpack
等Go Micro还提供了Sidecar等功能。这就可以使用以Go以外的语言编写的服务。Sidecar提供服务注册,gRPC编码/解码和HTTP处理程序。它支持多种语言。
Go Kit是用于在Go中构建微服务的编程工具包。与Go Micro不同,它是一个旨在导入二进制软件包的库。Go Kit遵循简单原则,例如:
在Go Kit中可以找到以下软件包:
basic
和JWT
Peter Bourgon的文章和演示幻灯片是对Go Kit最好的描述之一:
在“ Go + microservices”幻灯片中,可以找到使用Go Kit构建的服务体系结构的示例。为了快速参考,服务架构图如下所示:
Gizmo是《纽约时报》的微服务工具包。它提供了将服务和pubsub
守护进程放在一起的软件包。它公开了以下软件包:
Pubsub
软件包提供了与以下队列一起使用的接口:
因此,可以认为Gizmo位于Go Micro和Go Kit之间。它不是像Go Micro这样的完整“黑箱”。同时,它不像Go Kit那样原始。它提供了更高级别的构建组件,例如config和pubsub软件包。
Kite是用于在Go中开发微服务的框架。它公开了RPC客户端和服务器程序包。创建的服务会自动在服务发现系统Kontrol中注册。 Kontrol是用Kite编写的,它本身就是Kite服务。这意味着Kite微服务在其自己的环境中运行良好。如果需要将Kite微服务连接到另一个服务发现系统,则需要自定义。这是从列表中删除Kite并决定不审查此框架的主要原因。
使用四个类别来比较框架:
时间:2019/10/28
Go Micro | Go Kit | Gizmo | |
---|---|---|---|
License | Apache License 2.0 | MIT | Apache License 2.0 |
Created | Jan 2015 | Feb 2015 | Dec 2015 |
Release | 60 | 9 | 48 |
Author | Asim Aslam | Peter Bourgon | JP Robinson |
Stars | 9484 | 15190 | 2937 |
Open/Closed Issues | 35/400 | 56/370 | 12/52 |
Last Update | Oct 26, 2019 | Oct 18, 2019 | Oct 14, 2019 |
Go Micro和Go kit 提供了一个主页,其中包含正式的文档和示例。Gizmo框架没有提供可靠的文档,唯一的正式文档是仓库中的README文件。
有关Go Micro的大量信息和公告,请访问micro.mu,microhq和社交媒体(https://twitter.com/MicroHQ)。
如果使用Go Kit,最好的文档可在Peter Bourgon博客中找到。我发现的最好的例子之一是在ru-rocker博客中。
使用Gizmo,源代码提供了最佳的文档和示例。
综上所述,如果您来自NodeJS领域,并希望看到类似ExpressJS的教程,您将感到失望。另一方面,这是创建自己的教程的绝佳机会。
Go Kit是最流行的微服务框架,基于GitHub统计数据,它已超过1万五千颗星。它有大量的贡献者(161)和超过1600个分叉。最后,Go Kit受DigitalOcean支持。
拥有9484颗星,54个贡献者和993个分叉-Go Micro位居第二。 Sixt是Go Micro的最大赞助商之一。
Gizmo第三名。超过2937星,41个贡献者和188个分叉。由《纽约时报》支持和创建。
Go Kit在代码质量类别中排名第一。它具有近80%的代码覆盖率,并且具有出色的Go报告评级。Gizmo也具有出色的Go报告评级。但是它的代码覆盖率只有46%。Go Micro不提供覆盖范围信息,但确实具有很高的Go报告评级。
为了更好地了解框架,创建了三个简单的微服务,如下图所示:
这些服务都实现了一个业务逻辑:打招呼(greeting),当用户向服务中输入“名称”参数时,服务会发送问候语作为响应。所有服务都满足以下需求:
对于喜欢阅读源代码的人,可以在这里访问仓库源代码。
使用Go Micro创建服务所需要做的第一件事是定义protobuf描述。所有这三种服务都使用了相同的protobuf定义。创建了以下服务描述:
syntax = "proto3"; package pb; service Greeter { rpc Greeting(GreetingRequest) returns (GreetingResponse) {} } message GreetingRequest { string name = 1; } message GreetingResponse { string greeting = 2; }
这个接口包含一个方法Greeting,它有一个请求参数(name)和一个响应参数(greeting)
然后使用修改后的protoc从protobuf中生成服务接口,该proto生成器是Go Micro的一个分叉,并经过修改以支持该框架的某些功能。在打招呼服务中将所有这些连接在一起,此时,服务正在启动并在服务发现系统中注册,它仅支持gRPC传输协议。
package main import ( "log" pb "github.com/antklim/go-microservices/go-micro-greeter/pb" "github.com/micro/go-micro" "golang.org/x/net/context" ) // Greeter implements greeter service. type Greeter struct{} // Greeting method implementation. func (g *Greeter) Greeting(ctx context.Context, in *pb.GreetingRequest, out *pb.GreetingResponse) error { out.Greeting = "GO-MICRO Hello " + in.Name return nil } func main() { service := micro.NewService( micro.Name("go-micro-srv-greeter"), micro.Version("latest"), ) service.Init() pb.RegisterGreeterHandler(service.Server(), new(Greeter)) if err := service.Run(); err != nil { log.Fatal(err) } }
为了支持HTTP传输,必须添加其他模块。它将HTTP请求映射到protobuf定义的请求,并调用gRPC服务。然后,它将服务响应映射到HTTP响应,并将其响应返回给用户。
package main import ( "context" "encoding/json" "log" "net/http" proto "github.com/antklim/go-microservices/go-micro-greeter/pb" "github.com/micro/go-micro/client" web "github.com/micro/go-web" ) func main() { service := web.NewService( web.Name("go-micro-web-greeter"), ) service.HandleFunc("/greeting", func(w http.ResponseWriter, r *http.Request) { if r.Method == "GET" { var name string vars := r.URL.Query() names, exists := vars["name"] if !exists || len(names) != 1 { name = "" } else { name = names[0] } cl := proto.NewGreeterClient("go-micro-srv-greeter", client.DefaultClient) rsp, err := cl.Greeting(context.Background(), &proto.GreetingRequest{Name: name}) if err != nil { http.Error(w, err.Error(), 500) return } js, err := json.Marshal(rsp) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } w.Header().Set("Content-Type", "application/json") w.Write(js) return } }) if err := service.Init(); err != nil { log.Fatal(err) } if err := service.Run(); err != nil { log.Fatal(err) } }
一方面,Go Micro在后台处理了许多事情,例如在服务发现系统中进行注册。另一方面,很难创建纯HTTP服务。
在完成Go Micro之后,继续进行Go Kit服务的实现。我花了很多时间阅读Go Kit存储库中提供的代码示例。了解端点的概念花了很多时间。下一个耗时的难题是服务发现注册中心代码。在找到一个很好的例子之前,我无法实现它。
最后,创建了四个包用于:
服务逻辑实现如下所示,代码没有任何依赖关系,它只是实现逻辑。
package greeterservice // Service describe greetings service. type Service interface { Health() bool Greeting(name string) string } // GreeterService implementation of the Service interface. type GreeterService struct{} // Health implementation of the Service. func (GreeterService) Health() bool { return true } // Greeting implementation of the Service. func (GreeterService) Greeting(name string) (greeting string) { greeting = "GO-KIT Hello " + name return }
下一段代码显示了端点定义:
package greeterendpoint import ( "context" "github.com/go-kit/kit/log" "github.com/antklim/go-microservices/go-kit-greeter/pkg/greeterservice" "github.com/go-kit/kit/endpoint" ) // 端点集合收集打招呼服务的所有端点,它旨在用作辅助结构将所有端点收集到单个参数 type Endpoints struct { HealthEndpoint endpoint.Endpoint // used by Consul for the healthcheck GreetingEndpoint endpoint.Endpoint } // MakeServerEndpoints返回服务端点,并在所有提供的中间件中进行连线 func MakeServerEndpoints(s greeterservice.Service, logger log.Logger) Endpoints { var healthEndpoint endpoint.Endpoint { healthEndpoint = MakeHealthEndpoint(s) healthEndpoint = LoggingMiddleware(log.With(logger, "method", "Health"))(healthEndpoint) } var greetingEndpoint endpoint.Endpoint { greetingEndpoint = MakeGreetingEndpoint(s) greetingEndpoint = LoggingMiddleware(log.With(logger, "method", "Greeting"))(greetingEndpoint) } return Endpoints{ HealthEndpoint: healthEndpoint, GreetingEndpoint: greetingEndpoint, } } // MakeHealthEndpoint构造Health端点来包装服务 func MakeHealthEndpoint(s greeterservice.Service) endpoint.Endpoint { return func(ctx context.Context, request interface{}) (response interface{}, err error) { healthy := s.Health() return HealthResponse{Healthy: healthy}, nil } } // MakeHealthEndpoint构造Greeter端点来包装服务 func MakeGreetingEndpoint(s greeterservice.Service) endpoint.Endpoint { return func(ctx context.Context, request interface{}) (response interface{}, err error) { req := request.(GreetingRequest) greeting := s.Greeting(req.Name) return GreetingResponse{Greeting: greeting}, nil } } // Failer是应该由响应类型实现的接口 // 响应编码器可以检查响应是否为Failer,如果是Failer, // 判断是否失败,或者是否基于错误使用单独的写入路径对响应进行编码 type Failer interface { Failed() error } // HealthRequest收集Health方法的请求参数 type HealthRequest struct{} // HealthResponse收集Health方法的响应值 type HealthResponse struct { Healthy bool `json:"healthy,omitempty"` Err error `json:"err,omitempty"` } // Failed实现Failer接口 func (r HealthResponse) Failed() error { return r.Err } // GreetingRequest收集Greeting方法的请求参数 type GreetingRequest struct { Name string `json:"name,omitempty"` } // GreetingResponse收集Greeting方法的响应值 type GreetingResponse struct { Greeting string `json:"greeting,omitempty"` Err error `json:"err,omitempty"` } // Failed实现Failer接口 func (r GreetingResponse) Failed() error { return r.Err }
定义服务和端点后,通过不同的传输协议暴露端点。从HTTP传输开始:
package greetertransport import ( "context" "encoding/json" "errors" "net/http" "github.com/antklim/go-microservices/go-kit-greeter/pkg/greeterendpoint" "github.com/go-kit/kit/log" httptransport "github.com/go-kit/kit/transport/http" "github.com/gorilla/mux" ) var ( // 如果缺少预期的路径变量,则返回ErrBadRouting ErrBadRouting = errors.New("inconsistent mapping between route and handler") ) // NewHTTPHandler返回一个HTTP处理程序,该处理程序使一组端点在预定义路径上可用 func NewHTTPHandler(endpoints greeterendpoint.Endpoints, logger log.Logger) http.Handler { m := mux.NewRouter() options := []httptransport.ServerOption{ httptransport.ServerErrorEncoder(encodeError), httptransport.ServerErrorLogger(logger), } // GET /health retrieves service heath information // GET /greeting?name retrieves greeting m.Methods("GET").Path("/health").Handler(httptransport.NewServer( endpoints.HealthEndpoint, DecodeHTTPHealthRequest, EncodeHTTPGenericResponse, options..., )) m.Methods("GET").Path("/greeting").Handler(httptransport.NewServer( endpoints.GreetingEndpoint, DecodeHTTPGreetingRequest, EncodeHTTPGenericResponse, options..., )) return m } // DecodeHTTPHealthRequest method. func DecodeHTTPHealthRequest(_ context.Context, _ *http.Request) (interface{}, error) { return greeterendpoint.HealthRequest{}, nil } // DecodeHTTPGreetingRequest method. func DecodeHTTPGreetingRequest(_ context.Context, r *http.Request) (interface{}, error) { vars := r.URL.Query() names, exists := vars["name"] if !exists || len(names) != 1 { return nil, ErrBadRouting } req := greeterendpoint.GreetingRequest{Name: names[0]} return req, nil } func encodeError(_ context.Context, err error, w http.ResponseWriter) { w.WriteHeader(err2code(err)) json.NewEncoder(w).Encode(errorWrapper{Error: err.Error()}) } func err2code(err error) int { switch err { default: return http.StatusInternalServerError } } type errorWrapper struct { Error string `json:"error"` } // EncodeHTTPGenericResponse是`transport/http.EncodeResponseFunc`,它将响应编码为JSON编码到响应编写器 func EncodeHTTPGenericResponse(ctx context.Context, w http.ResponseWriter, response interface{}) error { if f, ok := response.(greeterendpoint.Failer); ok && f.Failed() != nil { encodeError(ctx, f.Failed(), w) return nil } w.Header().Set("Content-Type", "application/json; charset=utf-8") return json.NewEncoder(w).Encode(response) }
在开始gRPC端点实现之前,不需要protobuf定义。直接复制上面Go Micro服务的protobuf定义。但是对于Go Kit,使用默认的服务生成器来创建服务接口。
protoc greeter.proto --go_out=plugins=grpc:.
package greetertransport import ( "context" "github.com/antklim/go-microservices/go-kit-greeter/pb" "github.com/antklim/go-microservices/go-kit-greeter/pkg/greeterendpoint" "github.com/go-kit/kit/log" grpctransport "github.com/go-kit/kit/transport/grpc" oldcontext "golang.org/x/net/context" ) type grpcServer struct { greeter grpctransport.Handler } // NewGRPCServer使一组端点可用作gRPC GreeterServer func NewGRPCServer(endpoints greeterendpoint.Endpoints, logger log.Logger) pb.GreeterServer { options := []grpctransport.ServerOption{ grpctransport.ServerErrorLogger(logger), } return &grpcServer{ greeter: grpctransport.NewServer( endpoints.GreetingEndpoint, decodeGRPCGreetingRequest, encodeGRPCGreetingResponse, options..., ), } } // Greeting实现GreeterService接口的方法 func (s *grpcServer) Greeting(ctx oldcontext.Context, req *pb.GreetingRequest) (*pb.GreetingResponse, error) { _, res, err := s.greeter.ServeGRPC(ctx, req) if err != nil { return nil, err } return res.(*pb.GreetingResponse), nil } // encodeGRPCGreetingRequest是一个`transport/grpc.DecodeRequestFunc`,它将gRPC问候请求转换为用户域问候请求 func decodeGRPCGreetingRequest(_ context.Context, grpcReq interface{}) (interface{}, error) { req := grpcReq.(*pb.GreetingRequest) return greeterendpoint.GreetingRequest{Name: req.Name}, nil } // encodeGRPCGreetingResponse是`transport/grpc.EncodeResponseFunc`,它将用户域问候响应转换为gRPC问候响应 func encodeGRPCGreetingResponse(_ context.Context, response interface{}) (interface{}, error) { res := response.(greeterendpoint.GreetingResponse) return &pb.GreetingResponse{Greeting: res.Greeting}, nil }
最后,实现服务发现注册器:
package greetersd import ( "math/rand" "os" "strconv" "time" "github.com/go-kit/kit/log" "github.com/go-kit/kit/sd" consulsd "github.com/go-kit/kit/sd/consul" "github.com/hashicorp/consul/api" ) // ConsulRegister method. func ConsulRegister(consulAddress string, consulPort string, advertiseAddress string, advertisePort string) (registar sd.Registrar) { // Logging domain. var logger log.Logger { logger = log.NewLogfmtLogger(os.Stderr) logger = log.With(logger, "ts", log.DefaultTimestampUTC) logger = log.With(logger, "caller", log.DefaultCaller) } rand.Seed(time.Now().UTC().UnixNano()) // Service discovery domain. In this example we use Consul. var client consulsd.Client { consulConfig := api.DefaultConfig() consulConfig.Address = consulAddress + ":" + consulPort consulClient, err := api.NewClient(consulConfig) if err != nil { logger.Log("err", err) os.Exit(1) } client = consulsd.NewClient(consulClient) } check := api.AgentServiceCheck{ HTTP: "http://" + advertiseAddress + ":" + advertisePort + "/health", Interval: "10s", Timeout: "1s", Notes: "Basic health checks", } port, _ := strconv.Atoi(advertisePort) num := rand.Intn(100) // to make service ID unique asr := api.AgentServiceRegistration{ ID: "go-kit-srv-greeter-" + strconv.Itoa(num), //unique service ID Name: "go-kit-srv-greeter", Address: advertiseAddress, Port: port, Tags: []string{"go-kit", "greeter"}, Check: &check, } registar = consulsd.NewRegistrar(client, &asr, logger) return }
准备好所有构建块之后,我将它们连接到服务启动器中:
package main import ( "flag" "fmt" "net" "net/http" "os" "os/signal" "syscall" "text/tabwriter" "github.com/antklim/go-microservices/go-kit-greeter/pb" "google.golang.org/grpc" "github.com/antklim/go-microservices/go-kit-greeter/pkg/greeterendpoint" "github.com/antklim/go-microservices/go-kit-greeter/pkg/greetersd" "github.com/antklim/go-microservices/go-kit-greeter/pkg/greeterservice" "github.com/antklim/go-microservices/go-kit-greeter/pkg/greetertransport" "github.com/go-kit/kit/log" "github.com/oklog/oklog/pkg/group" ) func main() { fs := flag.NewFlagSet("greetersvc", flag.ExitOnError) var ( debugAddr = fs.String("debug.addr", ":9100", "Debug and metrics listen address") consulAddr = fs.String("consul.addr", "", "Consul Address") consulPort = fs.String("consul.port", "8500", "Consul Port") httpAddr = fs.String("http.addr", "", "HTTP Listen Address") httpPort = fs.String("http.port", "9110", "HTTP Listen Port") grpcAddr = fs.String("grpc-addr", ":9120", "gRPC listen address") ) fs.Usage = usageFor(fs, os.Args[0]+" [flags]") fs.Parse(os.Args[1:]) var logger log.Logger { logger = log.NewLogfmtLogger(os.Stderr) logger = log.With(logger, "ts", log.DefaultTimestampUTC) logger = log.With(logger, "caller", log.DefaultCaller) } var service greeterservice.Service { service = greeterservice.GreeterService{} service = greeterservice.LoggingMiddleware(logger)(service) } var ( endpoints = greeterendpoint.MakeServerEndpoints(service, logger) httpHandler = greetertransport.NewHTTPHandler(endpoints, logger) registar = greetersd.ConsulRegister(*consulAddr, *consulPort, *httpAddr, *httpPort) grpcServer = greetertransport.NewGRPCServer(endpoints, logger) ) var g group.Group { // The debug listener mounts the http.DefaultServeMux, and serves up // stuff like the Go debug and profiling routes, and so on. debugListener, err := net.Listen("tcp", *debugAddr) if err != nil { logger.Log("transport", "debug/HTTP", "during", "Listen", "err", err) os.Exit(1) } g.Add(func() error { logger.Log("transport", "debug/HTTP", "addr", *debugAddr) return http.Serve(debugListener, http.DefaultServeMux) }, func(error) { debugListener.Close() }) } { // The service discovery registration. g.Add(func() error { logger.Log("transport", "HTTP", "addr", *httpAddr, "port", *httpPort) registar.Register() return http.ListenAndServe(":"+*httpPort, httpHandler) }, func(error) { registar.Deregister() }) } { // The gRPC listener mounts the Go kit gRPC server we created. grpcListener, err := net.Listen("tcp", *grpcAddr) if err != nil { logger.Log("transport", "gRPC", "during", "Listen", "err", err) os.Exit(1) } g.Add(func() error { logger.Log("transport", "gRPC", "addr", *grpcAddr) baseServer := grpc.NewServer() pb.RegisterGreeterServer(baseServer, grpcServer) return baseServer.Serve(grpcListener) }, func(error) { grpcListener.Close() }) } { // This function just sits and waits for ctrl-C. cancelInterrupt := make(chan struct{}) g.Add(func() error { c := make(chan os.Signal, 1) signal.Notify(c, syscall.SIGINT, syscall.SIGTERM) select { case sig := <-c: return fmt.Errorf("received signal %s", sig) case <-cancelInterrupt: return nil } }, func(error) { close(cancelInterrupt) }) } logger.Log("exit", g.Run()) } func usageFor(fs *flag.FlagSet, short string) func() { return func() { fmt.Fprintf(os.Stderr, "USAGE\n") fmt.Fprintf(os.Stderr, " %s\n", short) fmt.Fprintf(os.Stderr, "\n") fmt.Fprintf(os.Stderr, "FLAGS\n") w := tabwriter.NewWriter(os.Stderr, 0, 2, 2, ' ', 0) fs.VisitAll(func(f *flag.Flag) { fmt.Fprintf(w, "\t-%s %s\t%s\n", f.Name, f.DefValue, f.Usage) }) w.Flush() fmt.Fprintf(os.Stderr, "\n") } }
在几个地方使用了日志记录中间件。它使我们能够将日志记录逻辑与主要服务/端点工作流分离。
package greeterservice import ( "time" "github.com/go-kit/kit/log" ) // ServiceMiddleware描述了服务中间件 type ServiceMiddleware func(Service) Service // LoggingMiddleware将记录器作为依赖项,并返回ServiceMiddleware。 func LoggingMiddleware(logger log.Logger) ServiceMiddleware { return func(next Service) Service { return loggingMiddleware{next, logger} } } type loggingMiddleware struct { Service logger log.Logger } func (m loggingMiddleware) Health() (healthy bool) { defer func(begin time.Time) { m.logger.Log( "method", "Health", "healthy", healthy, "took", time.Since(begin), ) }(time.Now()) healthy = m.Service.Health() return } func (m loggingMiddleware) Greeting(name string) (greeting string) { defer func(begin time.Time) { m.logger.Log( "method", "Greeting", "name", name, "greeting", greeting, "took", time.Since(begin), ) }(time.Now()) greeting = m.Service.Greeting(name) return }
package greeterendpoint import ( "context" "time" "github.com/go-kit/kit/endpoint" "github.com/go-kit/kit/log" ) // LoggingMiddleware返回一个端点中间件,该中间件记录每次调用的持续时间以及所产生的错误(如果有) func LoggingMiddleware(logger log.Logger) endpoint.Middleware { return func(next endpoint.Endpoint) endpoint.Endpoint { return func(ctx context.Context, request interface{}) (response interface{}, err error) { defer func(begin time.Time) { logger.Log("transport_error", err, "took", time.Since(begin)) }(time.Now()) return next(ctx, request) } } }
以与Go Kit类似的方式创建了Gizmo服务。为服务,端点,传输和服务发现注册器定义了四个包。服务实现和服务发现系统注册中心与Go Kit服务共享相同的代码。但是端点定义和传输实现必须根据Gizmo的功能来完成。
package greeterendpoint import ( "net/http" ocontext "golang.org/x/net/context" "github.com/NYTimes/gizmo/server" "github.com/antklim/go-microservices/gizmo-greeter/pkg/greeterservice" ) // Endpoints collects all of the endpoints that compose a greeter service. type Endpoints struct { HealthEndpoint server.JSONContextEndpoint GreetingEndpoint server.JSONContextEndpoint } // MakeServerEndpoints returns service Endoints func MakeServerEndpoints(s greeterservice.Service) Endpoints { healthEndpoint := MakeHealthEndpoint(s) greetingEndpoint := MakeGreetingEndpoint(s) return Endpoints{ HealthEndpoint: healthEndpoint, GreetingEndpoint: greetingEndpoint, } } // MakeHealthEndpoint constructs a Health endpoint. func MakeHealthEndpoint(s greeterservice.Service) server.JSONContextEndpoint { return func(ctx ocontext.Context, r *http.Request) (int, interface{}, error) { healthy := s.Health() return http.StatusOK, HealthResponse{Healthy: healthy}, nil } } // MakeGreetingEndpoint constructs a Greeting endpoint. func MakeGreetingEndpoint(s greeterservice.Service) server.JSONContextEndpoint { return func(ctx ocontext.Context, r *http.Request) (int, interface{}, error) { vars := r.URL.Query() names, exists := vars["name"] if !exists || len(names) != 1 { return http.StatusBadRequest, errorResponse{Error: "query parameter 'name' required"}, nil } greeting := s.Greeting(names[0]) return http.StatusOK, GreetingResponse{Greeting: greeting}, nil } } // HealthRequest collects the request parameters for the Health method. type HealthRequest struct{} // HealthResponse collects the response values for the Health method. type HealthResponse struct { Healthy bool `json:"healthy,omitempty"` } // GreetingRequest collects the request parameters for the Greeting method. type GreetingRequest struct { Name string `json:"name,omitempty"` } // GreetingResponse collects the response values for the Greeting method. type GreetingResponse struct { Greeting string `json:"greeting,omitempty"` } type errorResponse struct { Error string `json:"error"` }
如上所示,该代码段类似于Go Kit。主要区别在于应返回的接口类型:
package greetertransport import ( "context" "github.com/NYTimes/gizmo/server" "google.golang.org/grpc" "errors" "net/http" "github.com/NYTimes/gziphandler" pb "github.com/antklim/go-microservices/gizmo-greeter/pb" "github.com/antklim/go-microservices/gizmo-greeter/pkg/greeterendpoint" "github.com/sirupsen/logrus" ) type ( // TService will implement server.RPCService and handle all requests to the server. TService struct { Endpoints greeterendpoint.Endpoints } // Config is a struct to contain all the needed // configuration for our JSONService. Config struct { Server *server.Config } ) // NewTService will instantiate a RPCService with the given configuration. func NewTService(cfg *Config, endpoints greeterendpoint.Endpoints) *TService { return &TService{Endpoints: endpoints} } // Prefix returns the string prefix used for all endpoints within this service. func (s *TService) Prefix() string { return "" } // Service provides the TService with a description of the service to serve and // the implementation. func (s *TService) Service() (*grpc.ServiceDesc, interface{}) { return &pb.Greeter_serviceDesc, s } // Middleware provides an http.Handler hook wrapped around all requests. // In this implementation, we're using a GzipHandler middleware to // compress our responses. func (s *TService) Middleware(h http.Handler) http.Handler { return gziphandler.GzipHandler(h) } // ContextMiddleware provides a server.ContextHAndler hook wrapped around all // requests. This could be handy if you need to decorate the request context. func (s *TService) ContextMiddleware(h server.ContextHandler) server.ContextHandler { return h } // JSONMiddleware provides a JSONEndpoint hook wrapped around all requests. // In this implementation, we're using it to provide application logging and to check errors // and provide generic responses. func (s *TService) JSONMiddleware(j server.JSONContextEndpoint) server.JSONContextEndpoint { return func(ctx context.Context, r *http.Request) (int, interface{}, error) { status, res, err := j(ctx, r) if err != nil { server.LogWithFields(r).WithFields(logrus.Fields{ "error": err, }).Error("problems with serving request") return http.StatusServiceUnavailable, nil, errors.New("sorry, this service is unavailable") } server.LogWithFields(r).Info("success!") return status, res, nil } } // ContextEndpoints may be needed if your server has any non-RPC-able // endpoints. In this case, we have none but still need this method to // satisfy the server.RPCService interface. func (s *TService) ContextEndpoints() map[string]map[string]server.ContextHandlerFunc { return map[string]map[string]server.ContextHandlerFunc{} } // JSONEndpoints is a listing of all endpoints available in the TService. func (s *TService) JSONEndpoints() map[string]map[string]server.JSONContextEndpoint { return map[string]map[string]server.JSONContextEndpoint{ "/health": map[string]server.JSONContextEndpoint{ "GET": s.Endpoints.HealthEndpoint, }, "/greeting": map[string]server.JSONContextEndpoint{ "GET": s.Endpoints.GreetingEndpoint, }, } }
package greetertransport import ( pb "github.com/antklim/go-microservices/gizmo-greeter/pb" ocontext "golang.org/x/net/context" ) // Greeting implementation of the gRPC service. func (s *TService) Greeting(ctx ocontext.Context, r *pb.GreetingRequest) (*pb.GreetingResponse, error) { return &pb.GreetingResponse{Greeting: "Hola Gizmo RPC " + r.Name}, nil }
Go Kit和Gizmo之间的明显区别在于传输方式。 Gizmo提供了几种可以利用的服务类型。要做的就是将HTTP路径映射到端点定义。低级HTTP请求/响应处理由Gizmo处理。
Go Micro是启动微服务系统的最快方法。框架提供了许多功能。因此,无需重新发明轮子。但是,这种舒适性和速度会牺牲灵活性。更改或更新系统部件并不像Go Kit那样容易。并且将gRPC强制为一等的通信类型。
可能需要一些时间来熟悉Go Kit。它需要具备Go语言特性的丰富知识和软件架构方面的经验。另一方面,没有框架限制。所有部件均可独立更改和更新。
Gizmo位于Go Micro和Go Kit之间。它提供了一些更高级别的抽象,例如Service包。但是缺少文档和示例,这意味着不得不通读源代码以了解不同服务类型的工作方式。使用Gizmo比使用Go Kit容易。但是它不像Go Micro那样流畅。