use grpc+protobuf instead of http REST

master
shengxiang 9 years ago
parent 96883fc81e
commit be90e1b988

@ -38,12 +38,13 @@ There is a directory `showpid`
When run `gosuv showpid`, file `run` will be called.
# RPC Design
I decide to use protobuf in 2015-09-05
I decide to use [grpc](http://www.grpc.io/) in 2015-09-05
<https://github.com/grpc/grpc-go>
<https://github.com/golang/protobuf>
go get -u -v github.com/golang/protobuf/{proto,protoc-gen-go}
cd pbrpc; protoc --go_out=. *.proto
pbrpc/codegen.sh
# Design

@ -4,6 +4,7 @@ import (
"encoding/json"
"fmt"
"io/ioutil"
"net"
"net/http"
"os"
"os/exec"
@ -12,8 +13,12 @@ import (
"time"
"github.com/codegangsta/cli"
pb "github.com/codeskyblue/gosuv/gosuvpb"
"github.com/franela/goreq"
"github.com/golang/protobuf/proto"
"github.com/qiniu/log"
"golang.org/x/net/context"
"google.golang.org/grpc"
)
var (
@ -157,16 +162,39 @@ func buildpbURI(ctx *cli.Context) string {
func StopAction(ctx *cli.Context) {
log.Println(buildpbURI(ctx))
req := &pb.CtrlRequest{
Action: proto.String("stop"),
}
_ = req
}
func rpcDialer(addr string, timeout time.Duration) (conn net.Conn, err error) {
return net.DialTimeout("unix", addr, timeout)
}
func ShutdownAction(ctx *cli.Context) {
res, err := chttp("POST", fmt.Sprintf("http://%s:%d/api/shutdown",
ctx.GlobalString("host"), ctx.GlobalInt("port")))
sockPath := filepath.Join(GOSUV_HOME, "gosuv.sock")
conn, err := grpc.Dial(sockPath, grpc.WithDialer(rpcDialer), grpc.WithInsecure())
if err != nil {
log.Println("Already shutdown")
return
log.Fatal(err)
}
fmt.Println(res.Message)
defer conn.Close()
client := pb.NewGoSuvClient(conn)
res, err := client.Shutdown(context.Background(), &pb.NopRequest{})
if err != nil {
log.Fatal(err)
}
log.Println("Return code:", res.GetCode())
/*
res, err := chttp("POST", fmt.Sprintf("http://%s:%d/api/shutdown",
ctx.GlobalString("host"), ctx.GlobalInt("port")))
if err != nil {
log.Println("Already shutdown")
return
}
fmt.Println(res.Message)
*/
}
func VersionAction(ctx *cli.Context) {

@ -0,0 +1,5 @@
#!/bin/bash -x
#
cd $(dirname $0)
protoc --go_out=plugins=grpc:. *.proto
go install

@ -0,0 +1,183 @@
// Code generated by protoc-gen-go.
// source: gosuv.proto
// DO NOT EDIT!
/*
Package gosuvpb is a generated protocol buffer package.
It is generated from these files:
gosuv.proto
It has these top-level messages:
CtrlRequest
CtrlResponse
NopRequest
Response
*/
package gosuvpb
import proto "github.com/golang/protobuf/proto"
import fmt "fmt"
import math "math"
import (
context "golang.org/x/net/context"
grpc "google.golang.org/grpc"
)
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf
type CtrlRequest struct {
Action *string `protobuf:"bytes,1,req,name=action" json:"action,omitempty"`
Name *string `protobuf:"bytes,2,req,name=name" json:"name,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
func (m *CtrlRequest) Reset() { *m = CtrlRequest{} }
func (m *CtrlRequest) String() string { return proto.CompactTextString(m) }
func (*CtrlRequest) ProtoMessage() {}
func (m *CtrlRequest) GetAction() string {
if m != nil && m.Action != nil {
return *m.Action
}
return ""
}
func (m *CtrlRequest) GetName() string {
if m != nil && m.Name != nil {
return *m.Name
}
return ""
}
type CtrlResponse struct {
Value *string `protobuf:"bytes,1,opt,name=value" json:"value,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
func (m *CtrlResponse) Reset() { *m = CtrlResponse{} }
func (m *CtrlResponse) String() string { return proto.CompactTextString(m) }
func (*CtrlResponse) ProtoMessage() {}
func (m *CtrlResponse) GetValue() string {
if m != nil && m.Value != nil {
return *m.Value
}
return ""
}
type NopRequest struct {
XXX_unrecognized []byte `json:"-"`
}
func (m *NopRequest) Reset() { *m = NopRequest{} }
func (m *NopRequest) String() string { return proto.CompactTextString(m) }
func (*NopRequest) ProtoMessage() {}
type Response struct {
Code *int32 `protobuf:"varint,1,opt,name=code" json:"code,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
func (m *Response) Reset() { *m = Response{} }
func (m *Response) String() string { return proto.CompactTextString(m) }
func (*Response) ProtoMessage() {}
func (m *Response) GetCode() int32 {
if m != nil && m.Code != nil {
return *m.Code
}
return 0
}
// Reference imports to suppress errors if they are not otherwise used.
var _ context.Context
var _ grpc.ClientConn
// Client API for GoSuv service
type GoSuvClient interface {
Control(ctx context.Context, in *CtrlRequest, opts ...grpc.CallOption) (*CtrlResponse, error)
Shutdown(ctx context.Context, in *NopRequest, opts ...grpc.CallOption) (*Response, error)
}
type goSuvClient struct {
cc *grpc.ClientConn
}
func NewGoSuvClient(cc *grpc.ClientConn) GoSuvClient {
return &goSuvClient{cc}
}
func (c *goSuvClient) Control(ctx context.Context, in *CtrlRequest, opts ...grpc.CallOption) (*CtrlResponse, error) {
out := new(CtrlResponse)
err := grpc.Invoke(ctx, "/gosuvpb.GoSuv/Control", in, out, c.cc, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *goSuvClient) Shutdown(ctx context.Context, in *NopRequest, opts ...grpc.CallOption) (*Response, error) {
out := new(Response)
err := grpc.Invoke(ctx, "/gosuvpb.GoSuv/Shutdown", in, out, c.cc, opts...)
if err != nil {
return nil, err
}
return out, nil
}
// Server API for GoSuv service
type GoSuvServer interface {
Control(context.Context, *CtrlRequest) (*CtrlResponse, error)
Shutdown(context.Context, *NopRequest) (*Response, error)
}
func RegisterGoSuvServer(s *grpc.Server, srv GoSuvServer) {
s.RegisterService(&_GoSuv_serviceDesc, srv)
}
func _GoSuv_Control_Handler(srv interface{}, ctx context.Context, codec grpc.Codec, buf []byte) (interface{}, error) {
in := new(CtrlRequest)
if err := codec.Unmarshal(buf, in); err != nil {
return nil, err
}
out, err := srv.(GoSuvServer).Control(ctx, in)
if err != nil {
return nil, err
}
return out, nil
}
func _GoSuv_Shutdown_Handler(srv interface{}, ctx context.Context, codec grpc.Codec, buf []byte) (interface{}, error) {
in := new(NopRequest)
if err := codec.Unmarshal(buf, in); err != nil {
return nil, err
}
out, err := srv.(GoSuvServer).Shutdown(ctx, in)
if err != nil {
return nil, err
}
return out, nil
}
var _GoSuv_serviceDesc = grpc.ServiceDesc{
ServiceName: "gosuvpb.GoSuv",
HandlerType: (*GoSuvServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "Control",
Handler: _GoSuv_Control_Handler,
},
{
MethodName: "Shutdown",
Handler: _GoSuv_Shutdown_Handler,
},
},
Streams: []grpc.StreamDesc{},
}

@ -0,0 +1,22 @@
package gosuvpb;
message CtrlRequest {
required string action =1;
required string name = 2;
}
message CtrlResponse {
optional string value = 1;
}
message NopRequest {
}
message Response {
optional int32 code = 1;
}
service GoSuv {
rpc Control(CtrlRequest) returns (CtrlResponse) {};
rpc Shutdown(NopRequest) returns (Response) {};
}

@ -1,64 +0,0 @@
// Code generated by protoc-gen-go.
// source: gosuv.proto
// DO NOT EDIT!
/*
Package pbrpc is a generated protocol buffer package.
It is generated from these files:
gosuv.proto
It has these top-level messages:
CtrlRequest
CtrlResponse
*/
package pbrpc
import proto "github.com/golang/protobuf/proto"
import fmt "fmt"
import math "math"
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf
type CtrlRequest struct {
Name *string `protobuf:"bytes,1,req,name=name" json:"name,omitempty"`
Key *string `protobuf:"bytes,2,req,name=key" json:"key,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
func (m *CtrlRequest) Reset() { *m = CtrlRequest{} }
func (m *CtrlRequest) String() string { return proto.CompactTextString(m) }
func (*CtrlRequest) ProtoMessage() {}
func (m *CtrlRequest) GetName() string {
if m != nil && m.Name != nil {
return *m.Name
}
return ""
}
func (m *CtrlRequest) GetKey() string {
if m != nil && m.Key != nil {
return *m.Key
}
return ""
}
type CtrlResponse struct {
Value *string `protobuf:"bytes,1,opt,name=value" json:"value,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
func (m *CtrlResponse) Reset() { *m = CtrlResponse{} }
func (m *CtrlResponse) String() string { return proto.CompactTextString(m) }
func (*CtrlResponse) ProtoMessage() {}
func (m *CtrlResponse) GetValue() string {
if m != nil && m.Value != nil {
return *m.Value
}
return ""
}

@ -1,15 +0,0 @@
package pbrpc;
message CtrlRequest {
required string name = 1;
required string key = 2; // not actually required/guaranteed to be UTF-8
}
message CtrlResponse {
optional string value = 1;
}
service GoSUV{
rpc Control(CtrlRequest) returns (CtrlResponse) {
};
}

@ -3,13 +3,20 @@ package main
import (
"encoding/json"
"fmt"
"net"
"net/http"
"os"
"os/exec"
"path/filepath"
"sync"
"time"
pb "github.com/codeskyblue/gosuv/gosuvpb"
"github.com/golang/protobuf/proto"
"github.com/lunny/log"
"github.com/lunny/tango"
"golang.org/x/net/context"
"google.golang.org/grpc"
)
type JSONResponse struct {
@ -82,6 +89,27 @@ func shutdownHandler(w http.ResponseWriter, r *http.Request) {
})
}
type SuvServer struct {
lis net.Listener
}
func (s *SuvServer) Control(ctx context.Context, in *pb.CtrlRequest) (*pb.CtrlResponse, error) {
res := &pb.CtrlResponse{}
res.Value = proto.String("Hi")
return res, nil
}
func (s *SuvServer) Shutdown(ctx context.Context, in *pb.NopRequest) (*pb.Response, error) {
go func() {
time.Sleep(50 * time.Millisecond)
s.lis.Close()
os.Exit(2)
}()
res := &pb.Response{}
res.Code = proto.Int32(200)
return res, nil
}
func ServeAddr(host string, port int) error {
InitServer()
@ -94,6 +122,26 @@ func ServeAddr(host string, port int) error {
})
addr := fmt.Sprintf("%s:%d", host, port)
t.Run(addr)
wg := &sync.WaitGroup{}
wg.Add(2)
go func() {
t.Run(addr)
wg.Done()
}()
go func() {
grpcServ := grpc.NewServer()
pbServ := &SuvServer{}
pb.RegisterGoSuvServer(grpcServ, pbServ)
lis, err := net.Listen("unix", filepath.Join(GOSUV_HOME, "gosuv.sock"))
if err != nil {
log.Fatal(err)
}
pbServ.lis = lis
//defer lis.Close()
grpcServ.Serve(lis)
wg.Done()
}()
wg.Wait()
return fmt.Errorf("Address: %s has been used", addr)
}

Loading…
Cancel
Save