grpc-scaffolding

command module
v0.0.0-...-61578eb Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 25, 2022 License: MIT Imports: 4 Imported by: 0

README

grpc-scaffolding

grpc-scaffolding aims to scaffold grpc project quickly by generating code via the proto definition and go template.

The following is Go project layout scaffold generated:

├── Dockerfile
├── Makefile
├── .gitignore
├── .env.example
├── README.md
├── cmd
│   └── main.go
├── config
│   ├── config.go
│ 
├── internal/ should put business logic here.
├── pkg/
└── server
│   ├── handler
│   │     ├── health.go
│   │     ...
│   ├── grpc
│   │     ├── server.go
│   ├── server.go

if you define service proto at repo rpc-proto like:

...
// HealthService ...
service HealthService {
  // Ready ...
  rpc Ready (ReadyRequest) returns (ReadyResponse) {
    option (google.api.http) = {
      post: "/api/v1/ready"
      body: "*"
    };
  }
}

// ReadyRequest ...
message ReadyRequest{
}

// ReadyResponse ...
message ReadyResponse {
}

...
// ExampleService ...
service ExampleService {
  // Hello ...
  rpc Hello (HelloRequest) returns (HelloResponse) {
    option (google.api.http) = {
      post: "/api/v1/hello"
      body: "*"
    };
  }
}

// HelloRequest defines input to hello.
message HelloRequest{
  string msg = 1 [(validate.rules).string.min_len = 1];
}

// HelloResponse ...
message HelloResponse {
  string msg = 1;
}

project generated will has files like:

project-name/server/grpc/server.go


// Code generated by go generate; DO NOT EDIT.
// This file was generated by @generated
// source: codegen/grpcserver/tmpl/server.go.tmpl

package server

import (
	"context"
	"fmt"
	"net"

	http "net/http"

	"github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
	"os"
	"os/signal"
	"syscall"
	"time"

	promhttp "github.com/prometheus/client_golang/prometheus/promhttp"

	examplev1 "github.com/linhbkhn95/grpc-service/go/example/v1"
	healthv1 "github.com/linhbkhn95/grpc-service/go/health/v1"
	grpc "google.golang.org/grpc"
	"google.golang.org/grpc/credentials/insecure"
	"google.golang.org/protobuf/encoding/protojson"
)

type (
	// Server is server struct which contains both grpc and http server.
	Server struct {
		gRPC                 *grpc.Server
		mux                  *runtime.ServeMux
		cfg                  Config
		isRunningDevelopment bool
	}

	// Config hold http/grpc server config
	Config struct {
		GRPC ServerListen `yaml:"grpc" mapstructure:"grpc"`
		HTTP ServerListen `yaml:"http" mapstructure:"http"`
	}

	// ServerListen config for host/port socket listener
	// nolint:revive
	ServerListen struct {
		Host string `yaml:"host" mapstructure:"host"`
		Port int    `yaml:"port" mapstructure:"port"`
	}
)

// DefaultConfig return a default server config
func DefaultConfig() Config {
	return NewConfig(10443, 10080)
}

// NewConfig return a optional config with grpc port and http port.
func NewConfig(grpcPort, httpPort int) Config {
	return Config{
		GRPC: ServerListen{
			Host: "0.0.0.0",
			Port: grpcPort,
		},

		HTTP: ServerListen{
			Host: "0.0.0.0",
			Port: httpPort,
		},
	}
}

// String return socket listen DSN
func (l ServerListen) String() string {
	return fmt.Sprintf("%s:%d", l.Host, l.Port)
}

func NewServer(cfg Config, isRunningDevelopment bool, opt ...grpc.ServerOption) *Server {
	return &Server{
		gRPC: grpc.NewServer(opt...),
		mux: runtime.NewServeMux(
			runtime.WithMarshalerOption(runtime.MIMEWildcard,
				&runtime.JSONPb{
					MarshalOptions: protojson.MarshalOptions{
						UseProtoNames:   false,
						UseEnumNumbers:  false,
						EmitUnpopulated: true,
					},
					UnmarshalOptions: protojson.UnmarshalOptions{
						DiscardUnknown: true,
					},
				})),
		cfg:                  cfg,
		isRunningDevelopment: isRunningDevelopment,
	}
}
func (s *Server) Register(grpcServer ...interface{}) error {
	for _, srv := range grpcServer {
		switch _srv := srv.(type) {

		case healthv1.HealthServiceServer:
			healthv1.RegisterHealthServiceServer(s.gRPC, _srv)
			if err := healthv1.RegisterHealthServiceHandlerFromEndpoint(context.Background(), s.mux, s.cfg.GRPC.String(), []grpc.DialOption{grpc.WithTransportCredentials(insecure.NewCredentials())}); err != nil {
				return err
			}

		case examplev1.ExampleServiceServer:
			examplev1.RegisterExampleServiceServer(s.gRPC, _srv)
			if err := examplev1.RegisterExampleServiceHandlerFromEndpoint(context.Background(), s.mux, s.cfg.GRPC.String(), []grpc.DialOption{grpc.WithTransportCredentials(insecure.NewCredentials())}); err != nil {
				return err
			}

		default:
			return fmt.Errorf("Unknown GRPC Service to register %#v", srv)
		}
	}
	return nil
}

// Serve server listen for HTTP and GRPC
func (s *Server) Serve() error {
	stop := make(chan os.Signal, 1)
	errch := make(chan error)
	signal.Notify(stop, os.Interrupt, syscall.SIGINT, syscall.SIGTERM)

	httpMux := http.NewServeMux()

	httpMux.Handle("/metrics", promhttp.Handler())

	httpMux.Handle("/", s.mux)
	httpServer := http.Server{
		Addr:    s.cfg.HTTP.String(),
		Handler: httpMux,
	}
	go func() {
		if err := httpServer.ListenAndServe(); err != nil {
			errch <- err
		}
	}()

	go func() {
		listener, err := net.Listen("tcp", s.cfg.GRPC.String())
		if err != nil {
			errch <- err
			return
		}
		if err := s.gRPC.Serve(listener); err != nil {
			errch <- err
		}
	}()
	for {
		select {
		case <-stop:
			ctx, cancelFn := context.WithTimeout(context.Background(), 30*time.Second)
			defer cancelFn()
			s.gRPC.GracefulStop()

			if err := httpServer.Shutdown(ctx); err != nil {
				fmt.Println("failed to stop server: %w", err)
			}

			if !s.isRunningDevelopment {
				fmt.Println("Shutting down. Wait for 15 seconds")
				time.Sleep(15 * time.Second)
			}
			return nil
		case err := <-errch:
			return err
		}
	}
}

project-name/server/handler/health.go


package handler

import (
	"context"

	healthv1 "github.com/linhbkhn95/grpc-service/go/health/v1"
)

type HealthServer struct {
	healthv1.UnimplementedHealthServiceServer
}

func NewHealthServer() healthv1.HealthServiceServer {
	return &HealthServer{}
}

//TODO: implement methods of this service.

// Ready ...
func (s HealthServer) Ready(ctx context.Context, req *healthv1.ReadyRequest) (*healthv1.ReadyResponse, error) {
	return &healthv1.ReadyResponse{}, nil
}

project-name/server/handler/example.go


package handler

import (
	"context"

	examplev1 "github.com/linhbkhn95/grpc-service/go/example/v1"
)

type ExampleServer struct {
	examplev1.UnimplementedExampleServiceServer
}

func NewExampleServer() examplev1.ExampleServiceServer {
	return &ExampleServer{}
}

//TODO: implement methods of this service.

// SayHello will send hello term to server.
func (s ExampleServer) SayHello(ctx context.Context, req *examplev1.SayHelloRequest) (*examplev1.SayHelloResponse, error) {
	return &examplev1.SayHelloResponse{}, nil
}

// SayGoodbye will send goodbye term to server.
func (s ExampleServer) SayGoodbye(ctx context.Context, req *examplev1.SayGoodbyeRequest) (*examplev1.SayGoodbyeResponse, error) {
	return &examplev1.SayGoodbyeResponse{}, nil
}

Features

  • Easy generate project from proto definition
  • Provide layout standard and rpc server instance for implementation. Separate layer transport and service

Modules

  - layout

  - grpc_server

Usage

  1. install: go get github.com/linhbkhn95/grpc-scaffolding or go install github.com/linhbkhn95/grpc-scaffolding@latest if your terminal throw error like
go: downloading github.com/linhbkhn95/grpc-scaffolding v0.0.0-20220728042906-33c17ac9cdfd
go: github.com/linhbkhn95/grpc-scaffolding@latest: github.com/linhbkhn95/grpc-scaffolding@v0.0.0-20220728042906-33c17ac9cdfd: verifying module: github.com/linhbkhn95/grpc-scaffolding@v0.0.0-20220728042906-33c17ac9cdfd: reading https://sum.golang.org/lookup/github.com/!kyber!network/rpc-framework@v0.0.0-20220728042906-33c17ac9cdfd: 410 Gone
  server response:
  not found: github.com/linhbkhn95/grpc-scaffolding@v0.0.0-20220728042906-33c17ac9cdfd: invalid version: git ls-remote -q origin in /tmp/gopath/pkg/mod/cache/vcs/12330e8f12b843c19322388d9f726ba65c92be1dda46cd6a348f373be8edd50d: exit status 128:
  	fatal: could not read Username for 'https://github.com': terminal prompts disabled
  Confirm the import path was entered correctly.
  If this is a private repository, see https://golang.org/doc/faq#git_https for additional information.

please setup private repo via command: export GOPRIVATE=github.com/linhbkhn95/* and git config --global url.https://${{ PERSONAL_ACCESS_TOKEN }}@github.com/.insteadOf https://github.com/

  1. install grpc folder: run git clone git@github.com:linhbkhn95/rpc-proto.git.

  2. cd module need to use then run go generate.

  3. you can edit flag at header of file main.go before generate: go:generate go run gen.go grpc_server --rpc_protos=health/v1/health.proto.

Layout module

It is really rpc-framework which will init project and make layout for your project. It will read proto file from proto path which defined. It include all of GRPC interface. So, It can generate something which you need like (RPC instance, handler implementation ...)

Example: when run init project example which combine by two services health and example. You should run command: (should run in folder which contains rpc-proto dir)

rpc-framework --project_name=example --go_module_name=github.com/linhbkhn95/example --rpc_proto_dir=rpc-proto/proto --rpc_protos=health/v1/service.proto,example/v1/service.proto

When you run command above, you should see like something below.

2022/07/27 00:25:26 File was generate at example/config/config.go
2022/07/27 00:25:26 File was generate at example/server/server.go
2022/07/27 00:25:27 File was generate at example/server/grpc/server.go
2022/07/27 00:25:27 File was generate at example/server/handler/health.go
2022/07/27 00:25:27 File was generate at example/cmd/main.go
2022/07/27 00:25:27 File was generate at example/.gitignore
2022/07/27 00:25:27 File was generate at example/go.mod
2022/07/27 00:25:27 File was generate at example/Makefile
2022/07/27 00:25:27 File was generate at example/.gitignore
2022/07/27 00:25:27 Generated completed!
2022/07/27 00:25:27 installing dependencies ...
2022/07/27 00:25:27 installing grpc ecosystem ...
2022/07/27 00:25:27 installing viper...
2022/07/27 00:25:27 installing github.com/linhbkhn95/golang-british ...
2022/07/27 00:25:35 installing github.com/linhbkhn95/grpc-service ...
2022/07/27 00:25:40 installing google.golang.org/grpc ...
2022/07/27 00:25:41 installing github.com/grpc-ecosystem/go-grpc-middleware ...
2022/07/27 00:25:42 installing github.com/grpc-ecosystem/grpc-gateway/v2/runtime ...
2022/07/27 00:25:42 installing github.com/prometheus/client_golang/prometheus/promhttp ...
2022/07/27 00:25:43 installing google.golang.org/grpc ...
2022/07/27 00:25:43 installing google.golang.org/grpc/credentials/insecure ...
2022/07/27 00:25:44 installing google.golang.org/protobuf/encoding/protojson ...
2022/07/27 00:25:44 installing github.com/grpc-ecosystem/go-grpc-prometheus ...
2022/07/27 00:25:45 run go mod tidy ...
2022/07/27 00:25:45 install successfully ..

the result like here. https://github.com/linhbkhn95/grpc-scaffolding/tree/main/example

Module flags

  • Grpc server

    • rpc_protos: list service from project grpc, default value: health/v1/service.proto.
    • enable_gateway: flag use enable gateway for server, default value: false.
    • output_path: goal path then generate, default value: grpc_server/output/z_server_grpc.go.
    • rpc_proto_dir: Folder contain services of project, default value : ../rpc-proto/proto.
    • enable_metric: flag to enable metric by prometheus module. default value: false.
    • enable_http: fag to enable port http, default value: true.
  • Layout It will contain all of flags of grpc_server and add 2 flags flag is project_name and go_module_name . So, You should run command

rpc-framework --project_name=example --go_module_name=github.com/linhbkhn95/example --rpc_proto_dir=rpc-proto/proto --rpc_protos=health/v1/service.proto,example/v1/service.proto

Documentation

Overview

go generate

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL