https://github.com/linhbkhn95/grpc-scaffolding
grpc-scaffolding aims to scaffold grpc project quickly by generating code via the proto definition and go template.
https://github.com/linhbkhn95/grpc-scaffolding
framework go grpc hacktoberfest hacktoberfest2022 protobuf scaffolding template
Last synced: 2 months ago
JSON representation
grpc-scaffolding aims to scaffold grpc project quickly by generating code via the proto definition and go template.
- Host: GitHub
- URL: https://github.com/linhbkhn95/grpc-scaffolding
- Owner: linhbkhn95
- License: mit
- Created: 2022-10-13T02:43:58.000Z (almost 4 years ago)
- Default Branch: master
- Last Pushed: 2022-10-25T07:19:05.000Z (almost 4 years ago)
- Last Synced: 2024-06-21T10:58:52.663Z (about 2 years ago)
- Topics: framework, go, grpc, hacktoberfest, hacktoberfest2022, protobuf, scaffolding, template
- Language: Go
- Homepage:
- Size: 70.3 KB
- Stars: 2
- Watchers: 2
- Forks: 0
- Open Issues: 1
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
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:
```proto
...
// 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/`
2. install grpc folder: run `git clone git@github.com:linhbkhn95/rpc-proto.git`.
3. cd module need to use then run `go generate`.
4. 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
```