248 lines
5.4 KiB
Markdown
Raw Permalink Normal View History

# [fit] Protobuf & Code Generation
### 2016, by Manfred Touron (@moul)
---
# overview
* go-kit is an amazing framework to develop strong micro services
* but it requires a lot of boilerplate code
* return on experience on go-kit boilerplate code generation
---
# protobuf?
* limited to exchanges (methods and models)
* extendable with plugins
* contract-based
* universal
---
# code generation?
* the good old ./generate.sh bash script
* go:generate
* make
* protobuf + [protoc-gen-gotemplate](https://github.com/moul/protoc-gen-gotemplate)
---
# go-kit
* protobuf-first, rpc-first service framework in Golang
* abstract services, endpoints, transports
* requires a lot of boilerplate code in multiple packages
---
# example: `session.proto`
```protobuf
syntax = "proto3";
package session;
service SessionService {
rpc Login(LoginRequest) returns (LoginResponse) {}
}
message LoginRequest {
string username = 1;
string password = 2;
}
message LoginResponse {
string token = 1;
string err_msg = 2;
}
```
---
# example: `session.go`
```go
package sessionsvc
import (
"fmt"
"golang.org/x/net/context"
pb "github.com/moul/protoc-gen-gotemplate/examples/go-kit/services/session/gen/pb"
)
type Service struct{}
func New() pb.SessionServiceServer {
return &Service{}
}
func (svc *Service) Login(ctx context.Context, in *pb.LoginRequest) (*pb.LoginResponse, error) {
// custon code here
return nil, fmt.Errorf("not implemented")
}
```
---
##### example: `{{.File.Package}}/gen/transports/http/http.go.tmpl`
```go
// source: templates/{{.File.Package}}/gen/transports/http/http.go.tmpl
package {{.File.Package}}_httptransport
import (
gokit_endpoint "github.com/go-kit/kit/endpoint"
httptransport "github.com/go-kit/kit/transport/http"
endpoints "github.com/moul/protoc-gen-gotemplate/examples/go-kit/services/{{.File.Package}}/gen/endpoints"
pb "github.com/moul/protoc-gen-gotemplate/examples/go-kit/services/{{.File.Package}}/gen/pb"
)
```
```go
// result: services/user/gen/transports/http/http.go
package user_httptransport
import (
gokit_endpoint "github.com/go-kit/kit/endpoint"
httptransport "github.com/go-kit/kit/transport/http"
endpoints "github.com/moul/protoc-gen-gotemplate/examples/go-kit/services/user/gen/endpoints"
pb "github.com/moul/protoc-gen-gotemplate/examples/go-kit/services/user/gen/pb"
)
```
---
##### example: `{{.File.Package}}/gen/transports/http/http.go.tmpl`
```go
// source: templates/{{.File.Package}}/gen/transports/http/http.go.tmpl
{{range .Service.Method}}
func Make{{.Name}}Handler(ctx context.Context, svc pb.{{$file.Package | title}}ServiceServer, endpoint gokit_endpoint.Endpoint) *httptransport.Server {
return httptransport.NewServer(
ctx,
endpoint,
decode{{.Name}}Request,
encode{{.Name}}Response,
[]httptransport.ServerOption{}...,
)
}
{{end}}
```
```go
// result: services/user/gen/transports/http/http.go
func MakeGetUserHandler(ctx context.Context, svc pb.UserServiceServer, endpoint gokit_endpoint.Endpoint) *httptransport.Server {
return httptransport.NewServer(
ctx,
endpoint,
decodeGetUserRequest,
encodeGetUserResponse,
[]httptransport.ServerOption{}...,
)
}
```
---
##### example: `{{.File.Package}}/gen/transports/http/http.go.tmpl`
```go
// source: templates/{{.File.Package}}/gen/transports/http/http.go.tmpl
func RegisterHandlers(ctx context.Context, svc pb.{{$file.Package | title}}ServiceServer, mux *http.ServeMux, endpoints endpoints.Endpoints) error {
{{range .Service.Method}}
log.Println("new HTTP endpoint: \"/{{.Name}}\" (service={{$file.Package | title}})")
mux.Handle("/{{.Name}}", Make{{.Name}}Handler(ctx, svc, endpoints.{{.Name}}Endpoint))
{{end}}
return nil
}
```
```go
// result: services/user/gen/transports/http/http.go
func RegisterHandlers(ctx context.Context, svc pb.UserServiceServer, mux *http.ServeMux, endpoints endpoints.Endpoints) error {
log.Println("new HTTP endpoint: \"/CreateUser\" (service=User)")
mux.Handle("/CreateUser", MakeCreateUserHandler(ctx, svc, endpoints.CreateUserEndpoint))
log.Println("new HTTP endpoint: \"/GetUser\" (service=User)")
mux.Handle("/GetUser", MakeGetUserHandler(ctx, svc, endpoints.GetUserEndpoint))
return nil
}
```
---
#### `protoc --gogo_out=plugins=grpc:. ./services/*/*.proto`
---
#### `protoc --gotemplate_out=template_dir=./templates:services ./services/*/*.proto`
---
![fit](assets/session-wc.png)
---
![right fit](assets/wc.png)
## 3 services
## 6 methods
## 149 custom lines
## 1429 generated lines
## business focus
---
# generation usages
* go-kit boilerplate (see [examples/go-kit](https://github.com/moul/protoc-gen-gotemplate/tree/master/examples/go-kit))
* k8s configuration
* Dockerfile
* documentation
* unit-tests
* fun
---
# pros
* small custom codebase
* templates shipped with code
* hardly typed, no reflects
* genericity
* contrat terms (protobuf) respected
* not limited to a language
---
# cons
* the author needs to write its own templates
* sometimes difficult to generate valid code
* not enough helpers around the code generation yet
---
# improvement ideas
* Support protobufs extensions (i.e, annotations.probo)
* Generate one file from multiple services
* Add more helpers around the code generation
---
# conclusion
* Useful to keep everything standard
* The awesomeness of go-kit without the hassle of writing boilerplate code
* Always up-to-date with the contracts
---
# questions?
### github.com/moul/protoc-gen-gotemplate
### @moul