├── .gitignore ├── LICENSE ├── README.md ├── bench ├── http.go └── main.go ├── client └── main.go ├── proto ├── grpc-kvs.pb.go └── grpc-kvs.proto └── server └── main.go /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 2 | *.o 3 | *.a 4 | *.so 5 | 6 | # Folders 7 | _obj 8 | _test 9 | 10 | # Architecture specific extensions/prefixes 11 | *.[568vq] 12 | [568vq].out 13 | 14 | *.cgo1.go 15 | *.cgo2.c 16 | _cgo_defun.c 17 | _cgo_gotypes.go 18 | _cgo_export.* 19 | 20 | _testmain.go 21 | 22 | *.exe 23 | *.test 24 | *.prof 25 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 ono_matope 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # grpc-kvs 2 | This is an example project for grpc-go 3 | -------------------------------------------------------------------------------- /bench/http.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "io/ioutil" 7 | "log" 8 | "net/http" 9 | "sync" 10 | "time" 11 | ) 12 | 13 | type HTTP_KVS struct { 14 | mu sync.RWMutex 15 | elements map[string]string 16 | } 17 | 18 | type GetResponse struct { 19 | Value string 20 | } 21 | 22 | func (h *HTTP_KVS) ServeHTTP(w http.ResponseWriter, r *http.Request) { 23 | h.mu.RLock() 24 | defer h.mu.RUnlock() 25 | key := r.URL.Query().Get("key") 26 | 27 | if val, ok := h.elements[key]; ok { 28 | buf, err := json.Marshal(&GetResponse{Value: val}) 29 | if err != nil { 30 | w.WriteHeader(http.StatusInternalServerError) 31 | return 32 | } 33 | w.Write(buf) 34 | } else { 35 | w.WriteHeader(http.StatusNotFound) 36 | } 37 | 38 | return 39 | } 40 | 41 | func bench_http() { 42 | http.DefaultTransport.(*http.Transport).MaxIdleConnsPerHost = 100 43 | defer http.DefaultTransport.(*http.Transport).CloseIdleConnections() 44 | httpKvs := &HTTP_KVS{ 45 | elements: map[string]string{"key": "value"}, 46 | } 47 | http.Handle("/", httpKvs) 48 | go http.ListenAndServe(":8080", nil) 49 | time.Sleep(2 * time.Second) 50 | 51 | wg := &sync.WaitGroup{} 52 | ch := make(chan struct{}) 53 | for i := 0; i < concurrency; i++ { 54 | wg.Add(1) 55 | go func(ch chan struct{}) { 56 | defer wg.Done() 57 | for _ = range ch { 58 | //// HTTP I/F 59 | resp, err := http.Get("http://localhost:8080/?key=key") 60 | if err != nil { 61 | log.Fatal(err) 62 | } 63 | buf, err := ioutil.ReadAll(resp.Body) 64 | if err != nil { 65 | log.Fatal(err) 66 | } 67 | getResp := GetResponse{} 68 | if err := json.Unmarshal(buf, &getResp); err != nil { 69 | log.Fatal(err) 70 | } 71 | resp.Body.Close() 72 | } 73 | }(ch) 74 | } 75 | 76 | start := time.Now() 77 | for i := 0; i < numJobs; i++ { 78 | ch <- struct{}{} 79 | } 80 | 81 | close(ch) 82 | 83 | fmt.Printf("Waiting for workers...\n") 84 | wg.Wait() 85 | elapsed := time.Since(start) 86 | 87 | fmt.Printf("%d jobs in %0.2f seconds (%0.2f jobs/sec)\n", numJobs, float64(elapsed)/float64(time.Second), float64(numJobs)/(float64(elapsed)/float64(time.Second))) 88 | } 89 | -------------------------------------------------------------------------------- /bench/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "os" 7 | "sync" 8 | "time" 9 | 10 | pb "github.com/matope/grpc-kvs/proto" 11 | "golang.org/x/net/context" 12 | "google.golang.org/grpc" 13 | ) 14 | 15 | var ( 16 | addr = "localhost:50051" 17 | numJobs = 10000 18 | concurrency = 100 19 | ) 20 | 21 | func main() { 22 | if len(os.Args) < 1 { 23 | log.Fatal("require mode") 24 | } 25 | 26 | switch os.Args[1] { 27 | case "http": 28 | bench_http() 29 | case "grpc": 30 | bench_grpc() 31 | } 32 | } 33 | 34 | func bench_grpc() { 35 | conn, err := grpc.Dial(addr, grpc.WithInsecure()) 36 | if err != nil { 37 | log.Fatalf("did not connect: %v", err) 38 | } 39 | defer conn.Close() 40 | c := pb.NewKvsClient(conn) 41 | c.Put(context.Background(), &pb.PutRequest{Key: "key", Value: "value"}) 42 | time.Sleep(2 * time.Second) 43 | 44 | wg := &sync.WaitGroup{} 45 | ch := make(chan struct{}) 46 | for i := 0; i < concurrency; i++ { 47 | wg.Add(1) 48 | go func(ch chan struct{}) { 49 | defer wg.Done() 50 | for _ = range ch { 51 | 52 | // gRPC I/F 53 | _, err := c.Get(context.Background(), &pb.GetRequest{Key: "key"}) 54 | if err != nil { 55 | log.Fatal(err) 56 | } 57 | //fmt.Println(resp.Value) 58 | } 59 | }(ch) 60 | } 61 | 62 | start := time.Now() 63 | for i := 0; i < numJobs; i++ { 64 | ch <- struct{}{} 65 | } 66 | 67 | close(ch) 68 | 69 | fmt.Printf("Waiting for workers...\n") 70 | wg.Wait() 71 | elapsed := time.Since(start) 72 | 73 | fmt.Printf("%d jobs in %0.2f seconds (%0.2f jobs/sec)\n", numJobs, float64(elapsed)/float64(time.Second), float64(numJobs)/(float64(elapsed)/float64(time.Second))) 74 | } 75 | -------------------------------------------------------------------------------- /client/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "fmt" 6 | "io" 7 | "log" 8 | "os" 9 | "strings" 10 | 11 | pb "github.com/matope/grpc-kvs/proto" 12 | "golang.org/x/net/context" 13 | "google.golang.org/grpc" 14 | ) 15 | 16 | var addr = "localhost:50051" 17 | 18 | func main() { 19 | conn, err := grpc.Dial(addr, grpc.WithInsecure()) 20 | if err != nil { 21 | log.Fatalf("did not connect: %v", err) 22 | } 23 | defer conn.Close() 24 | c := pb.NewKvsClient(conn) 25 | 26 | scanner := bufio.NewScanner(os.Stdin) 27 | for { 28 | fmt.Print("> ") 29 | if !scanner.Scan() { 30 | os.Exit(0) 31 | } 32 | args := strings.Split(scanner.Text(), " ") 33 | 34 | switch args[0] { 35 | case "get": 36 | if len(args) < 1 { 37 | continue 38 | } 39 | var key = args[1] 40 | resp, err := c.Get(context.Background(), &pb.GetRequest{Key: key}) 41 | if err != nil { 42 | log.Printf("Error:%s", err.Error()) 43 | continue 44 | } 45 | fmt.Printf("Key:[%s] Value:[%s]\n", key, resp.Value) 46 | case "put": 47 | if len(args) < 3 { 48 | continue 49 | } 50 | key, value := args[1], args[2] 51 | _, err = c.Put(context.Background(), &pb.PutRequest{Key: key, Value: value}) 52 | if err != nil { 53 | log.Fatalf("Error:%v", err) 54 | continue 55 | } 56 | fmt.Println("OK") 57 | case "delete": 58 | if len(args) < 1 { 59 | continue 60 | } 61 | var key = args[1] 62 | _, err := c.Delete(context.Background(), &pb.DeleteRequest{Key: key}) 63 | if err != nil { 64 | log.Printf("Error:%s", err.Error()) 65 | continue 66 | } 67 | fmt.Println("OK") 68 | case "range": 69 | startKey := "" 70 | if len(args) > 1 { 71 | startKey = args[1] 72 | } 73 | rc, err := c.Range(context.Background(), &pb.RangeRequest{StartKey: startKey}) 74 | if err != nil { 75 | log.Fatalf("Error:%v", err) 76 | continue 77 | } 78 | for { 79 | resp, err := rc.Recv() 80 | if err == io.EOF { 81 | break 82 | } 83 | if err != nil { 84 | log.Println(err.Error()) 85 | break 86 | } 87 | fmt.Printf("Key: [%s] Value: [%s]\n", resp.Key, resp.Value) 88 | } 89 | case "watch": 90 | prefix := "" 91 | if len(args) > 1 { 92 | prefix = args[1] 93 | } 94 | rc, err := c.Watch(context.Background(), &pb.WatchRequest{Prefix: prefix}) 95 | if err != nil { 96 | log.Fatalf("Error:%v", err) 97 | continue 98 | } 99 | for { 100 | resp, err := rc.Recv() 101 | if err == io.EOF { 102 | break 103 | } 104 | if err != nil { 105 | log.Println(err.Error()) 106 | break 107 | } 108 | fmt.Printf("Key: [%s] Value: [%s]\n", resp.Key, resp.Value) 109 | } 110 | default: 111 | log.Printf("Unknown command. cmd:[%v]", args) 112 | } 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /proto/grpc-kvs.pb.go: -------------------------------------------------------------------------------- 1 | // Code generated by protoc-gen-go. 2 | // source: grpc-kvs.proto 3 | // DO NOT EDIT! 4 | 5 | /* 6 | Package proto is a generated protocol buffer package. 7 | 8 | It is generated from these files: 9 | grpc-kvs.proto 10 | 11 | It has these top-level messages: 12 | Entry 13 | GetRequest 14 | GetResponse 15 | PutRequest 16 | PutResponse 17 | DeleteRequest 18 | DeleteResponse 19 | RangeRequest 20 | WatchRequest 21 | */ 22 | package proto 23 | 24 | import proto1 "github.com/golang/protobuf/proto" 25 | import fmt "fmt" 26 | import math "math" 27 | 28 | import ( 29 | context "golang.org/x/net/context" 30 | grpc "google.golang.org/grpc" 31 | ) 32 | 33 | // Reference imports to suppress errors if they are not otherwise used. 34 | var _ = proto1.Marshal 35 | var _ = fmt.Errorf 36 | var _ = math.Inf 37 | 38 | type Entry struct { 39 | Key string `protobuf:"bytes,1,opt,name=key" json:"key,omitempty"` 40 | Value string `protobuf:"bytes,2,opt,name=value" json:"value,omitempty"` 41 | } 42 | 43 | func (m *Entry) Reset() { *m = Entry{} } 44 | func (m *Entry) String() string { return proto1.CompactTextString(m) } 45 | func (*Entry) ProtoMessage() {} 46 | func (*Entry) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} } 47 | 48 | type GetRequest struct { 49 | Key string `protobuf:"bytes,1,opt,name=key" json:"key,omitempty"` 50 | } 51 | 52 | func (m *GetRequest) Reset() { *m = GetRequest{} } 53 | func (m *GetRequest) String() string { return proto1.CompactTextString(m) } 54 | func (*GetRequest) ProtoMessage() {} 55 | func (*GetRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} } 56 | 57 | type GetResponse struct { 58 | Value string `protobuf:"bytes,1,opt,name=value" json:"value,omitempty"` 59 | } 60 | 61 | func (m *GetResponse) Reset() { *m = GetResponse{} } 62 | func (m *GetResponse) String() string { return proto1.CompactTextString(m) } 63 | func (*GetResponse) ProtoMessage() {} 64 | func (*GetResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{2} } 65 | 66 | type PutRequest struct { 67 | Key string `protobuf:"bytes,1,opt,name=key" json:"key,omitempty"` 68 | Value string `protobuf:"bytes,2,opt,name=value" json:"value,omitempty"` 69 | } 70 | 71 | func (m *PutRequest) Reset() { *m = PutRequest{} } 72 | func (m *PutRequest) String() string { return proto1.CompactTextString(m) } 73 | func (*PutRequest) ProtoMessage() {} 74 | func (*PutRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{3} } 75 | 76 | type PutResponse struct { 77 | } 78 | 79 | func (m *PutResponse) Reset() { *m = PutResponse{} } 80 | func (m *PutResponse) String() string { return proto1.CompactTextString(m) } 81 | func (*PutResponse) ProtoMessage() {} 82 | func (*PutResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{4} } 83 | 84 | type DeleteRequest struct { 85 | Key string `protobuf:"bytes,1,opt,name=key" json:"key,omitempty"` 86 | } 87 | 88 | func (m *DeleteRequest) Reset() { *m = DeleteRequest{} } 89 | func (m *DeleteRequest) String() string { return proto1.CompactTextString(m) } 90 | func (*DeleteRequest) ProtoMessage() {} 91 | func (*DeleteRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{5} } 92 | 93 | type DeleteResponse struct { 94 | } 95 | 96 | func (m *DeleteResponse) Reset() { *m = DeleteResponse{} } 97 | func (m *DeleteResponse) String() string { return proto1.CompactTextString(m) } 98 | func (*DeleteResponse) ProtoMessage() {} 99 | func (*DeleteResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{6} } 100 | 101 | type RangeRequest struct { 102 | StartKey string `protobuf:"bytes,1,opt,name=startKey" json:"startKey,omitempty"` 103 | MaxKeys int32 `protobuf:"varint,2,opt,name=maxKeys" json:"maxKeys,omitempty"` 104 | } 105 | 106 | func (m *RangeRequest) Reset() { *m = RangeRequest{} } 107 | func (m *RangeRequest) String() string { return proto1.CompactTextString(m) } 108 | func (*RangeRequest) ProtoMessage() {} 109 | func (*RangeRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{7} } 110 | 111 | type WatchRequest struct { 112 | Prefix string `protobuf:"bytes,1,opt,name=prefix" json:"prefix,omitempty"` 113 | } 114 | 115 | func (m *WatchRequest) Reset() { *m = WatchRequest{} } 116 | func (m *WatchRequest) String() string { return proto1.CompactTextString(m) } 117 | func (*WatchRequest) ProtoMessage() {} 118 | func (*WatchRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{8} } 119 | 120 | func init() { 121 | proto1.RegisterType((*Entry)(nil), "proto.Entry") 122 | proto1.RegisterType((*GetRequest)(nil), "proto.GetRequest") 123 | proto1.RegisterType((*GetResponse)(nil), "proto.GetResponse") 124 | proto1.RegisterType((*PutRequest)(nil), "proto.PutRequest") 125 | proto1.RegisterType((*PutResponse)(nil), "proto.PutResponse") 126 | proto1.RegisterType((*DeleteRequest)(nil), "proto.DeleteRequest") 127 | proto1.RegisterType((*DeleteResponse)(nil), "proto.DeleteResponse") 128 | proto1.RegisterType((*RangeRequest)(nil), "proto.RangeRequest") 129 | proto1.RegisterType((*WatchRequest)(nil), "proto.WatchRequest") 130 | } 131 | 132 | // Reference imports to suppress errors if they are not otherwise used. 133 | var _ context.Context 134 | var _ grpc.ClientConn 135 | 136 | // Client API for Kvs service 137 | 138 | type KvsClient interface { 139 | Get(ctx context.Context, in *GetRequest, opts ...grpc.CallOption) (*GetResponse, error) 140 | Put(ctx context.Context, in *PutRequest, opts ...grpc.CallOption) (*PutResponse, error) 141 | Delete(ctx context.Context, in *DeleteRequest, opts ...grpc.CallOption) (*DeleteResponse, error) 142 | Range(ctx context.Context, in *RangeRequest, opts ...grpc.CallOption) (Kvs_RangeClient, error) 143 | Watch(ctx context.Context, in *WatchRequest, opts ...grpc.CallOption) (Kvs_WatchClient, error) 144 | } 145 | 146 | type kvsClient struct { 147 | cc *grpc.ClientConn 148 | } 149 | 150 | func NewKvsClient(cc *grpc.ClientConn) KvsClient { 151 | return &kvsClient{cc} 152 | } 153 | 154 | func (c *kvsClient) Get(ctx context.Context, in *GetRequest, opts ...grpc.CallOption) (*GetResponse, error) { 155 | out := new(GetResponse) 156 | err := grpc.Invoke(ctx, "/proto.Kvs/Get", in, out, c.cc, opts...) 157 | if err != nil { 158 | return nil, err 159 | } 160 | return out, nil 161 | } 162 | 163 | func (c *kvsClient) Put(ctx context.Context, in *PutRequest, opts ...grpc.CallOption) (*PutResponse, error) { 164 | out := new(PutResponse) 165 | err := grpc.Invoke(ctx, "/proto.Kvs/Put", in, out, c.cc, opts...) 166 | if err != nil { 167 | return nil, err 168 | } 169 | return out, nil 170 | } 171 | 172 | func (c *kvsClient) Delete(ctx context.Context, in *DeleteRequest, opts ...grpc.CallOption) (*DeleteResponse, error) { 173 | out := new(DeleteResponse) 174 | err := grpc.Invoke(ctx, "/proto.Kvs/Delete", in, out, c.cc, opts...) 175 | if err != nil { 176 | return nil, err 177 | } 178 | return out, nil 179 | } 180 | 181 | func (c *kvsClient) Range(ctx context.Context, in *RangeRequest, opts ...grpc.CallOption) (Kvs_RangeClient, error) { 182 | stream, err := grpc.NewClientStream(ctx, &_Kvs_serviceDesc.Streams[0], c.cc, "/proto.Kvs/Range", opts...) 183 | if err != nil { 184 | return nil, err 185 | } 186 | x := &kvsRangeClient{stream} 187 | if err := x.ClientStream.SendMsg(in); err != nil { 188 | return nil, err 189 | } 190 | if err := x.ClientStream.CloseSend(); err != nil { 191 | return nil, err 192 | } 193 | return x, nil 194 | } 195 | 196 | type Kvs_RangeClient interface { 197 | Recv() (*Entry, error) 198 | grpc.ClientStream 199 | } 200 | 201 | type kvsRangeClient struct { 202 | grpc.ClientStream 203 | } 204 | 205 | func (x *kvsRangeClient) Recv() (*Entry, error) { 206 | m := new(Entry) 207 | if err := x.ClientStream.RecvMsg(m); err != nil { 208 | return nil, err 209 | } 210 | return m, nil 211 | } 212 | 213 | func (c *kvsClient) Watch(ctx context.Context, in *WatchRequest, opts ...grpc.CallOption) (Kvs_WatchClient, error) { 214 | stream, err := grpc.NewClientStream(ctx, &_Kvs_serviceDesc.Streams[1], c.cc, "/proto.Kvs/Watch", opts...) 215 | if err != nil { 216 | return nil, err 217 | } 218 | x := &kvsWatchClient{stream} 219 | if err := x.ClientStream.SendMsg(in); err != nil { 220 | return nil, err 221 | } 222 | if err := x.ClientStream.CloseSend(); err != nil { 223 | return nil, err 224 | } 225 | return x, nil 226 | } 227 | 228 | type Kvs_WatchClient interface { 229 | Recv() (*Entry, error) 230 | grpc.ClientStream 231 | } 232 | 233 | type kvsWatchClient struct { 234 | grpc.ClientStream 235 | } 236 | 237 | func (x *kvsWatchClient) Recv() (*Entry, error) { 238 | m := new(Entry) 239 | if err := x.ClientStream.RecvMsg(m); err != nil { 240 | return nil, err 241 | } 242 | return m, nil 243 | } 244 | 245 | // Server API for Kvs service 246 | 247 | type KvsServer interface { 248 | Get(context.Context, *GetRequest) (*GetResponse, error) 249 | Put(context.Context, *PutRequest) (*PutResponse, error) 250 | Delete(context.Context, *DeleteRequest) (*DeleteResponse, error) 251 | Range(*RangeRequest, Kvs_RangeServer) error 252 | Watch(*WatchRequest, Kvs_WatchServer) error 253 | } 254 | 255 | func RegisterKvsServer(s *grpc.Server, srv KvsServer) { 256 | s.RegisterService(&_Kvs_serviceDesc, srv) 257 | } 258 | 259 | func _Kvs_Get_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error) (interface{}, error) { 260 | in := new(GetRequest) 261 | if err := dec(in); err != nil { 262 | return nil, err 263 | } 264 | out, err := srv.(KvsServer).Get(ctx, in) 265 | if err != nil { 266 | return nil, err 267 | } 268 | return out, nil 269 | } 270 | 271 | func _Kvs_Put_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error) (interface{}, error) { 272 | in := new(PutRequest) 273 | if err := dec(in); err != nil { 274 | return nil, err 275 | } 276 | out, err := srv.(KvsServer).Put(ctx, in) 277 | if err != nil { 278 | return nil, err 279 | } 280 | return out, nil 281 | } 282 | 283 | func _Kvs_Delete_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error) (interface{}, error) { 284 | in := new(DeleteRequest) 285 | if err := dec(in); err != nil { 286 | return nil, err 287 | } 288 | out, err := srv.(KvsServer).Delete(ctx, in) 289 | if err != nil { 290 | return nil, err 291 | } 292 | return out, nil 293 | } 294 | 295 | func _Kvs_Range_Handler(srv interface{}, stream grpc.ServerStream) error { 296 | m := new(RangeRequest) 297 | if err := stream.RecvMsg(m); err != nil { 298 | return err 299 | } 300 | return srv.(KvsServer).Range(m, &kvsRangeServer{stream}) 301 | } 302 | 303 | type Kvs_RangeServer interface { 304 | Send(*Entry) error 305 | grpc.ServerStream 306 | } 307 | 308 | type kvsRangeServer struct { 309 | grpc.ServerStream 310 | } 311 | 312 | func (x *kvsRangeServer) Send(m *Entry) error { 313 | return x.ServerStream.SendMsg(m) 314 | } 315 | 316 | func _Kvs_Watch_Handler(srv interface{}, stream grpc.ServerStream) error { 317 | m := new(WatchRequest) 318 | if err := stream.RecvMsg(m); err != nil { 319 | return err 320 | } 321 | return srv.(KvsServer).Watch(m, &kvsWatchServer{stream}) 322 | } 323 | 324 | type Kvs_WatchServer interface { 325 | Send(*Entry) error 326 | grpc.ServerStream 327 | } 328 | 329 | type kvsWatchServer struct { 330 | grpc.ServerStream 331 | } 332 | 333 | func (x *kvsWatchServer) Send(m *Entry) error { 334 | return x.ServerStream.SendMsg(m) 335 | } 336 | 337 | var _Kvs_serviceDesc = grpc.ServiceDesc{ 338 | ServiceName: "proto.Kvs", 339 | HandlerType: (*KvsServer)(nil), 340 | Methods: []grpc.MethodDesc{ 341 | { 342 | MethodName: "Get", 343 | Handler: _Kvs_Get_Handler, 344 | }, 345 | { 346 | MethodName: "Put", 347 | Handler: _Kvs_Put_Handler, 348 | }, 349 | { 350 | MethodName: "Delete", 351 | Handler: _Kvs_Delete_Handler, 352 | }, 353 | }, 354 | Streams: []grpc.StreamDesc{ 355 | { 356 | StreamName: "Range", 357 | Handler: _Kvs_Range_Handler, 358 | ServerStreams: true, 359 | }, 360 | { 361 | StreamName: "Watch", 362 | Handler: _Kvs_Watch_Handler, 363 | ServerStreams: true, 364 | }, 365 | }, 366 | } 367 | 368 | var fileDescriptor0 = []byte{ 369 | // 289 bytes of a gzipped FileDescriptorProto 370 | 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x6c, 0x91, 0x3d, 0x4f, 0xc3, 0x30, 371 | 0x10, 0x86, 0x13, 0xa2, 0x04, 0xb8, 0x7c, 0x50, 0x0c, 0x48, 0x25, 0x42, 0x08, 0x85, 0xa5, 0x0b, 372 | 0x11, 0x1f, 0x03, 0x7f, 0x00, 0xc4, 0xd0, 0x05, 0x75, 0x61, 0x36, 0xd5, 0x51, 0x50, 0x4b, 0x12, 373 | 0x6c, 0x27, 0x6a, 0xff, 0x3c, 0x22, 0xb1, 0x5d, 0xc7, 0x2d, 0x9d, 0x22, 0xdf, 0x3d, 0xe7, 0x37, 374 | 0xcf, 0x19, 0x92, 0x19, 0xab, 0xa6, 0x37, 0xf3, 0x86, 0xe7, 0x15, 0x2b, 0x45, 0x49, 0x7c, 0xf9, 375 | 0xc9, 0xae, 0xc1, 0x7f, 0x2e, 0x04, 0x5b, 0x91, 0x10, 0xbc, 0x39, 0xae, 0x86, 0xee, 0x95, 0x3b, 376 | 0x3a, 0x24, 0x31, 0xf8, 0x0d, 0x5d, 0xd4, 0x38, 0xdc, 0xeb, 0x8e, 0xd9, 0x39, 0xc0, 0x0b, 0x8a, 377 | 0x09, 0xfe, 0xd4, 0xc8, 0xc5, 0x06, 0x99, 0x5d, 0x40, 0x28, 0x5b, 0xbc, 0x2a, 0x0b, 0x8e, 0xfd, 378 | 0xa0, 0xea, 0x8e, 0x00, 0x5e, 0xeb, 0x9d, 0x83, 0xdb, 0x11, 0x31, 0x84, 0x92, 0x54, 0xf7, 0xb4, 379 | 0xd7, 0xc6, 0x4f, 0xb8, 0x40, 0x81, 0x3b, 0x43, 0x07, 0x90, 0xac, 0xbb, 0x9a, 0xbf, 0x83, 0x68, 380 | 0x42, 0x8b, 0x99, 0xc1, 0x07, 0x70, 0xc0, 0x05, 0x65, 0x62, 0x6c, 0xf2, 0x8e, 0x60, 0xff, 0x9b, 381 | 0x2e, 0xdb, 0x33, 0x97, 0x89, 0x7e, 0x76, 0x09, 0xd1, 0x1b, 0x15, 0xd3, 0xcf, 0xf5, 0x48, 0x02, 382 | 0x41, 0xc5, 0xf0, 0xe3, 0x6b, 0xa9, 0x06, 0xee, 0x7f, 0x5d, 0xf0, 0xc6, 0x0d, 0x27, 0x39, 0x78, 383 | 0xad, 0x21, 0x39, 0x56, 0x7b, 0xcb, 0xfb, 0x45, 0xa4, 0xc4, 0x2e, 0xe9, 0x1f, 0x71, 0x3a, 0xbe, 384 | 0x35, 0x31, 0x7c, 0xef, 0x6f, 0x78, 0x5b, 0xd4, 0x21, 0x8f, 0x10, 0x28, 0x19, 0x72, 0xaa, 0xfb, 385 | 0x1b, 0xe6, 0xe9, 0xd9, 0x56, 0xd5, 0x0a, 0xf2, 0xa5, 0x33, 0x39, 0xd1, 0x84, 0xbd, 0x81, 0x34, 386 | 0xd2, 0x45, 0xf9, 0xba, 0x99, 0x73, 0xeb, 0x76, 0xbc, 0x14, 0x36, 0xbc, 0xad, 0xff, 0x9f, 0x7f, 387 | 0x0f, 0x64, 0xe1, 0xe1, 0x2f, 0x00, 0x00, 0xff, 0xff, 0xad, 0xbb, 0xda, 0x7a, 0x3a, 0x02, 0x00, 388 | 0x00, 389 | } 390 | -------------------------------------------------------------------------------- /proto/grpc-kvs.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package proto; 4 | 5 | service Kvs { 6 | rpc Get(GetRequest) returns (GetResponse) {} 7 | rpc Put(PutRequest) returns (PutResponse) {} 8 | rpc Delete(DeleteRequest) returns (DeleteResponse) {} 9 | rpc Range(RangeRequest) returns (stream Entry) {} 10 | rpc Watch(WatchRequest) returns (stream Entry) {} 11 | } 12 | 13 | message Entry { 14 | string key = 1; 15 | string value = 2; 16 | } 17 | 18 | message GetRequest { string key = 1; } 19 | message GetResponse { string value = 1; } 20 | 21 | message PutRequest { string key = 1; string value = 2; } 22 | message PutResponse {} 23 | 24 | message DeleteRequest { string key = 1; } 25 | message DeleteResponse {} 26 | 27 | message RangeRequest { string startKey = 1; int32 maxKeys = 2; } 28 | 29 | message WatchRequest { string prefix = 1; } 30 | 31 | -------------------------------------------------------------------------------- /server/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "net" 7 | "sort" 8 | "strings" 9 | "sync" 10 | 11 | pb "github.com/matope/grpc-kvs/proto" 12 | "golang.org/x/net/context" 13 | "google.golang.org/grpc" 14 | "google.golang.org/grpc/codes" 15 | ) 16 | 17 | var port = ":50051" 18 | 19 | func main() { 20 | lis, err := net.Listen("tcp", port) 21 | if err != nil { 22 | log.Fatalf("failed to listen: %v", err) 23 | } 24 | s := grpc.NewServer() 25 | pb.RegisterKvsServer(s, NewKvsServer()) 26 | s.Serve(lis) 27 | } 28 | 29 | type kvsServer struct { 30 | elements map[string]string 31 | mu sync.RWMutex 32 | chans map[chan pb.Entry]struct{} 33 | } 34 | 35 | func NewKvsServer() *kvsServer { 36 | return &kvsServer{ 37 | elements: make(map[string]string), 38 | chans: make(map[chan pb.Entry]struct{}), 39 | } 40 | } 41 | 42 | func (s *kvsServer) Get(ctx context.Context, r *pb.GetRequest) (*pb.GetResponse, error) { 43 | s.mu.RLock() 44 | defer s.mu.RUnlock() 45 | if val, ok := s.elements[r.Key]; ok { 46 | return &pb.GetResponse{ 47 | Value: val, 48 | }, nil 49 | } 50 | return &pb.GetResponse{}, grpc.Errorf(codes.NotFound, "element not found value=[%s]", r.Key) 51 | } 52 | 53 | func (s *kvsServer) Put(ctx context.Context, r *pb.PutRequest) (*pb.PutResponse, error) { 54 | s.mu.Lock() 55 | defer s.mu.Unlock() 56 | s.elements[r.Key] = r.Value 57 | 58 | // Notify updation 59 | for c := range s.chans { 60 | c <- pb.Entry{Key: r.Key, Value: r.Value} 61 | } 62 | return &pb.PutResponse{}, nil 63 | } 64 | 65 | func (s *kvsServer) Delete(ctx context.Context, r *pb.DeleteRequest) (*pb.DeleteResponse, error) { 66 | s.mu.Lock() 67 | defer s.mu.Unlock() 68 | delete(s.elements, r.Key) 69 | 70 | // Notify deletion 71 | for c := range s.chans { 72 | c <- pb.Entry{Key: r.Key} 73 | } 74 | 75 | return &pb.DeleteResponse{}, nil 76 | } 77 | 78 | func (s *kvsServer) Range(r *pb.RangeRequest, rs pb.Kvs_RangeServer) error { 79 | s.mu.RLock() 80 | defer s.mu.RUnlock() 81 | 82 | // sort and filter keys of elements 83 | keys := make([]string, 0, len(s.elements)) 84 | for k := range s.elements { 85 | if k < r.StartKey { 86 | continue 87 | } 88 | keys = append(keys, k) 89 | } 90 | sort.Strings(keys) 91 | 92 | for _, k := range keys { 93 | if err := rs.Send(&pb.Entry{Key: k, Value: s.elements[k]}); err != nil { 94 | return err 95 | } 96 | } 97 | return nil 98 | } 99 | 100 | func (s *kvsServer) Watch(r *pb.WatchRequest, ws pb.Kvs_WatchServer) error { 101 | ech := make(chan pb.Entry) 102 | s.mu.Lock() 103 | s.chans[ech] = struct{}{} 104 | s.mu.Unlock() 105 | fmt.Println("Added New Watcher", ech) 106 | 107 | defer func() { 108 | s.mu.Lock() 109 | delete(s.chans, ech) 110 | s.mu.Unlock() 111 | close(ech) 112 | fmt.Println("Deleted Watcher", ech) 113 | }() 114 | 115 | for e := range ech { 116 | if !strings.HasPrefix(e.Key, r.Prefix) { 117 | continue 118 | } 119 | err := ws.Send(&e) 120 | if err != nil { 121 | return err 122 | } 123 | } 124 | return nil 125 | } 126 | --------------------------------------------------------------------------------