# README
example
import "github.com/go-orion/Orion/example"
Overview
Lets create a minimal service with Orion
API Interface
Your service starts with the API interface, In orion we define all API interfaces as a protobuf file (stringsvc/stringproto/stringproto.proto)
syntax = "proto3";
package stringproto;
service StringService{
rpc Upper (UpperRequest) returns (UpperResponse){
}
rpc Count (CountRequest) returns (CountResponse) {
}
}
message UpperRequest {
string msg = 1;
}
message UpperResponse {
string msg = 1;
}
message CountRequest{
string msg = 1;
}
message CountResponse{
int64 count = 1;
}
Above protobuf file describes a service called "StringService" and defines the endpoints "Upper" and "Count", As we can see all our API Contracts are clearly laid out and makes it easy for other services to integrate with our service
Generating Service Code
we can execute
protoc -I . stringproto.proto --go_out=plugins=grpc:. --orion_out=.
to generate protobuf/orion code, you will see following files are generated
stringproto.pb.go
stringproto.pb.orion.go
Service Lifecycle
Orion manages lifecycle of the service by ServiceFactory (stringsvc/service/init.go)
package service
import "github.com/go-orion/Orion/orion"
type svcFactory struct{}
func (s *svcFactory) NewService(orion.Server) interface{} {
return &svc{}
}
func (s *svcFactory) DisposeService(svc interface{}) {
//do nothing
}
func GetFactory() orion.ServiceFactory {
return &svcFactory{}
}
"NewService" is called when a new service is initialized, "DisposeService" is called when a service is disposed. A new service is initialized and older service is disposed every time config reloads.
Implementing Service
we need to implement the "StringServiceServer" interface generated by protoc plugin, this is where our application logic lives (stringsvc/service/service.go)
package service
import (
"context"
"strings"
proto "github.com/go-orion/Orion/example/stringsvc/stringproto"
)
// svc implements proto.StringServiceServer
type svc struct{}
func (s *svc) Upper(ctx context.Context, req *proto.UpperRequest) (*proto.UpperResponse, error) {
resp := new(proto.UpperResponse)
resp.Msg = strings.ToUpper(req.GetMsg())
return resp, nil
}
func (s *svc) Count(ctx context.Context, req *proto.CountRequest) (*proto.CountResponse, error) {
resp := new(proto.CountResponse)
resp.Count = int64(len(req.GetMsg()))
return resp, nil
}
Building Server
in only a few lines of code we can get a server started serving gRPC and HTTP (stringsvc/cmd/server/main.go)
package main
import (
"github.com/go-orion/Orion/example/stringsvc/service"
proto "github.com/go-orion/Orion/example/stringsvc/stringproto"
"github.com/go-orion/Orion/orion"
)
func main() {
server := orion.GetDefaultServer("StringService")
proto.RegisterStringServiceOrionServer(service.GetFactory(), server)
server.Start()
server.Wait()
}
Running Server
we can start the server by running "go run cmd/server/main.go"
ᐅ go run cmd/server/main.go
2018/02/01 16:20:57 msg Reading config
2018/02/01 16:20:57 Config config could not be read Config File "StringService" Not Found in "[. /opt/config]"
2018/02/01 16:20:57 HTTPListnerPort 9282
2018/02/01 16:20:57 gRPCListnerPort 9281
2018/02/01 16:20:57 HystrixPort 9283
2018/02/01 16:20:57 warning rollbar token is empty not initializing rollbar
___ ____ ___ ___ _ _
/ _ \| _ \|_ _/ _ \| \ | |
| | | | |_) || | | | | \| |
| |_| | _ < | | |_| | |\ |
\___/|_| \_\___\___/|_| \_|
2018/02/01 16:20:57 GRPC server starting
2018/02/01 16:20:57 PprofPort 9284
Mapped URLs:
[POST] /stringservice/upper
[POST] /stringservice/count
Orion automatically generates and initializes HTTP/gRPC servers based on our defined proto as we can see the path for our endpoints Upper and Count are as follows
[POST] /stringservice/upper
[POST] /stringservice/count
Making HTTP Calls
Lets call Upper
ᐅ curl -X POST -i -d '{"msg":"Hello World"}' <a href="http://127.0.0.1:9282/stringservice/upper">http://127.0.0.1:9282/stringservice/upper</a>
HTTP/1.1 200 OK
Content-Type: application/json
Date: Thu, 01 Feb 2018 08:27:32 GMT
Content-Length: 21
{"msg":"HELLO WORLD"}
By default orion responds in "application/json" for HTTP calls, We can even ask for response in "protobuf" by setting the Accept HTTP header
ᐅ curl -X POST -i -H "Accept:application/protobuf" -d '{"msg":"Hello World"}' <a href="http://127.0.0.1:9282/stringservice/upper">http://127.0.0.1:9282/stringservice/upper</a>
HTTP/1.1 200 OK
Content-Type: application/octet-stream
Date: Thu, 01 Feb 2018 08:30:21 GMT
Content-Length: 13
// binary data omitted
HELLO WORLD
Orion also logs response time and errors for each API call
2018/02/01 16:27:32 path /stringservice/upper method POST error <nil> took 132.96µs
2018/02/01 16:30:21 path /stringservice/upper method POST error <nil> took 766.984µs
Making gRPC calls
we can build a client using StringServiceClient generated by protoc grpc plugin (stringsvc/cmd/client/main.go)
package main
import (
"context"
"log"
proto "github.com/go-orion/Orion/example/stringsvc/stringproto"
"google.golang.org/grpc"
)
const (
address = "127.0.0.1:9281"
)
func main() {
// establish connection
conn, err := grpc.Dial(address, grpc.WithInsecure())
if err != nil {
log.Fatalf("did not connect: %v", err)
}
defer conn.Close()
client := proto.NewStringServiceClient(conn)
uppercase(client)
}
func uppercase(client proto.StringServiceClient) {
r := new(proto.UpperRequest)
r.Msg = "Hello World"
log.Println("making gRPC calls for Upper")
resp, err := client.Upper(context.Background(), r)
log.Println("resp", resp)
log.Println("error", err)
}
On running this client we can see
ᐅ go run cmd/client/main.go
2018/02/01 16:50:06 making gRPC calls for Upper
2018/02/01 16:50:06 resp msg:"HELLO WORLD"
2018/02/01 16:50:06 error <nil>
we will see the following on server
2018/02/01 16:50:06 method /stringproto.StringService/Upper error <nil> took 19.764µs
Imported Packages
No packages beyond the Go standard library are imported.
Index
Package files
Generated by godoc2ghmd