Skip to content

Commit

Permalink
Create Simple RPC example
Browse files Browse the repository at this point in the history
  • Loading branch information
aaronchen committed Aug 17, 2020
1 parent 59bff89 commit 5def9e1
Show file tree
Hide file tree
Showing 4 changed files with 218 additions and 4 deletions.
128 changes: 127 additions & 1 deletion README.md
@@ -1,5 +1,131 @@
# GRPC-GO-Sandbox

> [Basics Tutorial](https://grpc.io/docs/languages/go/basics/) @ gRPC.io
## simple RPC

### 1. 使用 proto 定義 service

在 proto 檔中定義 Service:

```protobuf
service RouteGuide {
// A simple RPC
// A feature with an empty name is returned if there's no feature at the given position
rpc GetFeature(Point) returns (Feature) {}
}
```

### 2. 產生 client 和 server 的程式碼

有了 proto 檔後,只需要使用 protocol buffer 的 `protoc` 工具就能夠將自動產生對應程式語言的檔案,例如這裡會是 `routeguide.pb.go`

```bash
$ protoc -I routeguide/ routeguide/routeguide.proto --go_out=plugins=grpc:routeguide --go_opt=paths=source_relative
```
```

在這個檔案中將會包含
1. 用來自動產生(populate)、序列化(serialize)和取得 request / response message types 的 protocol buffer 程式碼
2. 給 client 用來使用的 interface type(或稱 stub),以此呼叫定義在 RouteGuide service 中的方法
3. 給 server 用來實作的 interface type

### 3. 建立 server

gRPC Server 要做的兩件事:

1. **Implementing Service**:根據 proto 檔中對於 service interface 的定義進行實作,也就是服務真正要做些什麼
2. **Starting the server**:啟動一個 gRPC 伺服器來監聽 clients 發送進來的請求,並且派送到正確的 service 去執行

#### Implementing Service

這段是用來實作 service:

```go
// pb 是 protocol buffer 的簡稱
import pb "sandbox/grpc-go-sandbox/routeguide"

// STEP 1-1:定義 routeGuideServer 的 struct
type routeGuideServer struct {
pb.UnimplementedRouteGuideServer
savedFeatures []*pb.Feature
}

// STEP 1-2:根據 proto 中的 service 建立實作方式
// 在 proto 中有定義這個 service 會接收 point 最為參數,並且會回傳 Feature
func (s *routeGuideServer) GetFeature(ctx context.Context, point *pb.Point) (
*pb.Feature, error,
) {
for _, feature := range s.savedFeatures {
if proto.Equal(feature.Location, point) {
return feature, nil
}
}

// No feature was found, return an unnamed feature
return &pb.Feature{Location: point}, nil
}
```

#### Starting the server

這段是用來啟動 gRPC server:

```go
func main() {
// STEP 2-1:定義要監聽的 port 號
lis, err := net.Listen("tcp", fmt.Sprintf("localhost:%d", 3000))
if err != nil {
log.Fatalf("failed to listed: %v", err)
}

// STEP 2-2:使用 gRPC 的 NewServer 方法來建立 gRPC Server 的實例
grpcServer := grpc.NewServer()

// STEP 2-3:在 gRPC Server 中註冊 service 的實作
// 使用 proto 提供的 RegisterRouteGuideServer 方法,並將 routeGuideServer 作為參數傳入
pb.RegisterRouteGuideServer(grpcServer, &routeGuideServer{})

// STEP 2-4:啟動 grpcServer,並阻塞在這裡直到該程序被 kill 或 stop
err = grpcServer.Serve(lis)
if err != nil {
log.Fatalf("failed to serve: %v", err)
}
}
```

### Creating the client

Client 要做得事情包含:

1. Creating the stub:要使用 service 的方法,需要建立一個 gRPC channel 來和 server 溝通。
2. 呼叫 service methods

#### Creating the stub

只需要使用 `grpc.Dial(<serverAddr>)` 即可建立與 server 的 channel:

```go
conn, err := grpc.Dial(*serverAddr)
if err != nil {
// ...
}
defer conn.Close()
```

一旦 gRPC 的 channel 建立好後,需要 client stub 來執行 RPCs。我們可以使用由 `*.pb.go` 檔(由 proto 檔產生)中,提供 `NewRouteGuideClient` 方法:

```go
import "sandbox/grpc-go-sandbox/routeguide"

client := routeGuide.NewRouteGuideClient(conn)
```

#### Calling service methods

在 gPRC-Go 中, RPCs 會以阻塞/同步的方式運算,也就是說一個 RPC 發出去之後,會等待伺服器的回應,不論是的正確的回應或錯誤。





33 changes: 33 additions & 0 deletions client/client.go
@@ -0,0 +1,33 @@
package main

import (
"context"
"google.golang.org/grpc"
"log"
pb "sandbox/grpc-go-sandbox/routeguide"
)

var serverAddr = "localhost:3000"

func main() {
// STEP 1:creating the client stub
// STEP 1-1:與 gRPC server 建立 channel
// 如果沒有使用安全連線的話,在 options 的地方要加上 grpc.WIthInsecure()
conn, err := grpc.Dial(serverAddr, grpc.WithInsecure())
if err != nil {
log.Fatalf("faild to dial: %v", err)
}
defer conn.Close()

// STEP 1-2:使用 proto 所提供的 NewRouteGuideClient 方法並帶入參數 conn 以來建立 client
client := pb.NewRouteGuideClient(conn)

// STEP 2:呼叫 Service Methods
// 透過 context.Context 物件,讓我們在需要時可以改變 RPC 的行為,像是立即執行 time-out/cancel 一個 RPC
feature, err := client.GetFeature(context.Background(), &pb.Point{Latitude: 409146138, Longitude: -746188906})
if err != nil {
log.Fatalf("faild to getFeature")
}

log.Println(feature)
}
6 changes: 3 additions & 3 deletions go.mod
Expand Up @@ -3,11 +3,11 @@ module sandbox/grpc-go-sandbox
go 1.14

require (
github.com/golang/protobuf v1.4.2 // indirect
github.com/golang/protobuf v1.4.2
golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc // indirect
golang.org/x/sys v0.0.0-20200812155832-6a926be9bd1d // indirect
golang.org/x/text v0.3.3 // indirect
google.golang.org/genproto v0.0.0-20200814021100-8c09557e8a18 // indirect
google.golang.org/grpc v1.31.0 // indirect
google.golang.org/protobuf v1.25.0 // indirect
google.golang.org/grpc v1.31.0
google.golang.org/protobuf v1.25.0
)
55 changes: 55 additions & 0 deletions server/server.go
@@ -0,0 +1,55 @@
package main

import (
"context"
"fmt"
"log"
"net"
pb "sandbox/grpc-go-sandbox/routeguide"

"google.golang.org/grpc"
"google.golang.org/protobuf/proto"
)

// STEP 1-1:定義 routeGuideServer 的 struct
type routeGuideServer struct {
pb.UnimplementedRouteGuideServer
savedFeatures []*pb.Feature
}

// STEP 1-2:根據 proto 中的 service 建立實作方式
// 在 proto 中有定義這個 service 會接收 point 最為參數,並且會回傳 Feature
func (s *routeGuideServer) GetFeature(ctx context.Context, point *pb.Point) (
*pb.Feature, error,
) {
for _, feature := range s.savedFeatures {
if proto.Equal(feature.Location, point) {
return feature, nil
}
}

// No feature was found, return an unnamed feature
log.Println("No feature was found, return an unnamed feature.")
return &pb.Feature{Location: point}, nil
}

func main() {
// STEP 2-1:定義要監聽的 port 號
lis, err := net.Listen("tcp", fmt.Sprintf("localhost:%d", 3000))
if err != nil {
log.Fatalf("failed to listed: %v", err)
}

// STEP 2-2:使用 gRPC 的 NewServer 方法來建立 gRPC Server 的實例
grpcServer := grpc.NewServer()

// STEP 2-3:在 gRPC Server 中註冊 service 的實作
// 使用 proto 提供的 RegisterRouteGuideServer 方法,並將 routeGuideServer 作為參數傳入
pb.RegisterRouteGuideServer(grpcServer, &routeGuideServer{})

// STEP 2-4:啟動 grpcServer,並阻塞在這裡直到該程序被 kill 或 stop
err = grpcServer.Serve(lis)
if err != nil {
log.Fatalf("failed to serve: %v", err)
}
}

0 comments on commit 5def9e1

Please sign in to comment.