Implementasi gRPC Di Golang Dan Windows
Tags: grpc, go, windows
Published: 2023-01-27 10:35:47

Foto oleh <a href="https://unsplash.com/ja/@steve_j?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Steve Johnson</a> dari <a href="https://unsplash.com/photos/hokONTrHIAQ?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Unsplash</a>
  
## Tentang RPC
Pada proses pengembangan aplikasi, proses mengintegrasikan komponen-komponen seperti _database_ maupun sistem eksternal menjadi salah satu bagian yang sulit dipisahkan. Proses integrasi ini lazimnya menggunakan _API_ (_Application Programming Interface_) dari _database_ atau sistem eksternal tertentu. Misalnya, ketika ingin mengintegrasikan _database MySQL_, kita perlu mengetahui perintah-perintah yang telah disediakan oleh _MySQL_ baik lewat _CLI_ (_Command Line Interface_) atau _database connector_ dari bahasa pemrograman yang kita gunakan supaya kita bisa memanipulasi data di dalam _database_ tersebut.

Cara lain yang lebih populer untuk mengintegrasikan aplikasi adalah menggunakan _RPC_ atau _HTTP/JSON API_. _RPC_ bisa digunakan untuk mengintegrasikan aplikasi jika performa menjadi pertimbangan, serta kita memiliki kendali penuh terhadap prosedur dari _service_ yang kita kelola, sedangkan _HTTP/JSON API_ digunakan jika aplikasi yang dikembangkan memperimbangkan kemudahan proses pengembangan serta aplikasi tersebut memiliki rencana untuk dikembangkan oleh orang lain di masa depan.

## Tentang gRPC
gRPC merupakan _RPC framework_ yang dikembangkan oleh Google. Seperti RPC pada umumnya, gRPC bekerja seperti memanggil fungsi atau prosedur dari suatu mesin oleh mesin lain secara _remote_.

![Arsitektur gRPC](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ji0y6giosbo8iracqjhn.png)

gRPC menggunakan `protobuf` atau _protocol buffer_ sebagai format atau kontrak yang digunakan untuk berkomunikasi antara _client_/_stub_ dengan _server_. Isi dari file `protobuf` ini adalah `service` yang merupakan method yang akan dipanggil oleh _client_, serta `message` yang merupakan data atau parameter yang diterima oleh `service`. File `protobuf` ini harus di-_compile_ ke dalam bahasa pemrograman yang ingin kita gunakan. Pada gambar di atas, dengan menggunakan file `protobuf` yang sama, gRPC _server_ yang ditulis menggunakan bahasa C++ bisa berkomunikasi dengan gRPC _client_ yang ditulis menggunakan bahasa Java atau Ruby.

Ada beberapa daur hidup atau _life cycle_ penggunaan gRPC yang bisa kita gunakan, diantaranya :
1. _Unary_ : Proses ini berjalan satu arah, di mana _client_ akan memanggil method/_service_ pada _server_, dan _server_ akan mengembalikan _response_ jika _service_ tersebut selesai diproses.
2. _Server/Client Streaming_ : Proses ini bisa berjalan baik di sisi _server_ atau _client_, dan proses ini mirip dengan _unary_. Hanya saja, data yang dikirim oleh _server_ atau _client_ dikirim secara kontinu/_streaming_ hingga tidak ada yang bisa dikirim data dari _server_ atau _client_.
3. _Bidirectional Streaming_ : Proses ini berjalan secara dua arah, di mana baik _server_ atau _client_ akan mengirim data hingga tidak ada data yang bisa dikirim atau salah satu dari _server_ atau _client_ menghentikan proses pengiriman data.

## Praktik
### Mengunduh Protobuf Compiler
Hal pertama yang perlu dipersiapkan adalah _protobuf compiler_. _Compiler_ tersebut berguna untuk men-_generate_ kode bahasa pemrograman dari file `.proto` yang kita buat. Teman-teman bisa mengunjungi [tautan ini](https://developers.google.com/protocol-buffers/docs/downloads) untuk mengunduh _compiler protobuf_ berdasarkan _operating system_ yang teman-teman gunakan. Artikel ini menggunakan protobuf compiler untuk windows 64 bit versi `3.21.12`.

Setelah teman-teman berhasil mengunduh protobuf compiler dan mengekstrak file zip tersebut, langkah selanjutnya adalah menambahkan _path_ folder `bin` _protobuf compiler_ yang telah diunduh ke _environment variable_. Folder `bin` yang dimaksud adalah seperti ini.

![Folder Bin](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/s6jomu1a1dcdpj8cmyjp.png)

Copy path executable file tersebut, lalu paste ke `Path` pada environment. Untuk menambahkan path executable file protobuf compiler ke environment variable, silakan teman-teman bisa cari `Edit the system and environment variable` di search bar windows teman-teman. Kemudian pilih `Path` pada bagian `User variables for admin`, tekan `New`, dan paste path bin folder protobuf compiler. Langkah-langkah tersebut seperti ini.

![Environment Variable](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bf34ptu1kr6vs16seefd.png)

![Environment Variable](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/doal4m8qhjp5l6kc5n91.png)

![Environment Variable](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/g4rju7d74fuifd3a4pbe.png)

Untuk memeriksa apakah _protobuf compiler_ sudah terpasang dengan benar, silakan teman-teman buka `cmd` dan ketik `protoc`. Jika _protobuf compiler_ sudah terpasang dengan benar, maka tampilannya akan seperti ini.

![Protobuf Compiler](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/va79auqgho0xs5ic8bqb.png)

### Implementasi
Silakan teman-teman buat project/folder baru dengan struktur seperti ini.

![Struktur Folder](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dkmo9925x365n0mk6lzg.png)

Pada file `student.proto`, kita akan menuliskan `message` serta `service` yang akan di-generate oleh _protobuf compiler_. Isi file `student.proto` tersebut sebagai berikut.

```
syntax = "proto3";

option go_package = "./../pb";

import "google/protobuf/empty.proto";

package pb;

message StudentRequest{
    string name = 1;
    int32 id = 2;
}

service Students{
    rpc RegisterStudent(StudentRequest) returns (google.protobuf.Empty);
}
```

Maksud dari file proto di atas dijelaskan pada tabel di bawah.

| Potongan Kode  | Penjelasan   |
|---|---|
| `syntax = "proto3";`  | Menentukan versi protobuf yang digunakan. Pada contoh ini, versi yang digunakan adalah versi 3  |
| `option go_package = "./../pb";`  | Menentukan di mana file protobuf yang di-generate oleh protobuf compiler akan di simpan. Pada contoh ini, seluruh generated file protobuf akan disimpan di dalam folder `pb` yang letaknya relatif terhadap lokasi file `.proto`  |
| `import "google/protobuf/empty.proto";`  | Melakukan import file proto dari package/folder lain  |
| `package pb;`  | Menentukan nama package dari file proto yang di-generate oleh protobuf compiler  |
| `message StudentRequest{}`  | Mendefinisikan message/payload yang akan digunakan. Setelah file proto berhasil di-generate, untuk bahasa `Go`, protobuf compiler akan membuat struct dengan nama `StudentRequest`  |
| `service Students{}`  | Mendefinisikan procedure/service yang akan dipanggil/invoke. Protobuf compiler akan men-generate interface `StudentServer` dan `StudentClient` dengan isi interface berupa method yang didefinisikan di dalam `service Students{}` |
| `rpc RegisterStudent(StudentRequest) returns (google.protobuf.Empty);` | Mendefiniskan method/contract yang akan digunakan oleh client dan server. Protobuf compiler akan men-generate method `RegisterStudent` yang akan di-implement oleh `StudentServer` dan `StudentClient`  |

Untuk menghasilkan _generated file_, kita perlu meng-_ompile_ file proto di atas dengan command sebagai berikut :

`protoc --go_out=. --go-grpc_out=. *.proto`

Perintah di atas dijalankan pada direktori di mana file proto berada. Gambar di bawah ini menunjukkan file proto yang berhasil dikompilasi.

![File Proto Yang Berhasil Dikompilasi](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/54lsjcc74jibuhhfzv44.png)

### Implementasi Dalam Golang
File proto yang telah dikompilasi kemudian bisa digunakan menggunakan bahasa pemrograman yang didukung oleh gRPC dan Protobuf (bahasa pemrograman yang didukung gRPC bisa dilihat [di sini](https://grpc.io/docs/languages/) dan bahasa pemrograman yang didukung Protocol BUffer bisa dilihat [di sini](https://developers.google.com/protocol-buffers/docs/reference/overview)).

Di dalam file `student_grpc.pb.go`, kita akan menemukan interface sebagai berikut :

```Go
type StudentsClient interface {
	RegisterStudent(ctx context.Context, in *StudentRequest, opts ...grpc.CallOption) (*emptypb.Empty, error)
}
...
type StudentsServer interface {
	RegisterStudent(context.Context, *StudentRequest) (*emptypb.Empty, error)
	mustEmbedUnimplementedStudentsServer()
}
```

Interface `StudentsServer` dan `StudentsClient` merupakan `service Students{}` dan method `RegisterStudent` merupakan `rpc RegisterStudent` yang di-generate oleh file proto yang telah kita buat. Interface tersebut harus kita implementasi baik di client atau server kita. Pada artikel ini, kita akan mengimplementasi `StudentServer`. Implementasi tersebut kita tulis dalam file `server/main.go`

```Go
package main

import (
	"context"
	"go-grpc-unary/common/pb"
	"log"
	"net"
	"os"
	"os/signal"
	"syscall"

	"google.golang.org/grpc"
	"google.golang.org/protobuf/types/known/emptypb"
)

type Server struct {
	pb.UnimplementedStudentsServer
}

const PORT = ":9000"

func (s *Server) RegisterStudent(ctx context.Context, in *pb.StudentRequest) (*emptypb.Empty, error) {
	log.Println(in.Name)
	log.Println(in.Id)
	return new(emptypb.Empty), nil
}

func initGrpcServer() {
	l, err := net.Listen("tcp", PORT)
	if err != nil {
		log.Fatalf("unable to create tcp connection at port %v with error %v \n", PORT, err)
	}

	srv := grpc.NewServer()
	pb.RegisterStudentsServer(srv, &Server{})
	srv.Serve(l)
}

func main() {
	done := make(chan os.Signal, 1)
	signal.Notify(done, syscall.SIGINT, syscall.SIGTERM)
	go initGrpcServer()
	<-done
}
```

### Pengujian Menggunakan Postman
Pada artikel ini, kita akan menggunakan Postman untuk menguji gRPC endpoint yang telah kita buat.

Pertama, kita perlu membuat `gRPC Request` seperti pada gambar.

![New gRPC Request](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rtkurrppxqnrtvc4vus2.png)

Kemudian, kita melakukan import file proto yang telah kita definisikan. Perhatikan bagian `Import paths` yang ditandai merah. Bagian tersebut kita isi dengan path di mana kita meng-import file proto lain pada file proto yang kita tulis. Pada file proto, kita menuliskan `import "google/protobuf/empty.proto";`, di mana kita meng-import file `empty.proto`. File tersebut merupakan file bawaan dari protobuf yang terletak di dalam folder `include/google/protobuf` (silakan kembali ke bagian **Mengunduh Protocol Buffer** untuk mengetahui lokasi folder tersebut).

![Import Path](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gyq3d3ygbsdrmqm00nxa.png) 

Kemudian, pilih `save without importing` untuk menyelesaikan proses import file proto.

![Ready To Go](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0iaroq8jxhtdj8x5cq0w.png)

Isikan server URL dengan `localhost:9000`. Pada bagian `Select a method`, kita dapat melihat gRPC contract/method yang telah kita definisikan di file proto, pada hal ini `rpc RegisterStudent`. Pilih method tersebut, dan isikan data yang akan dikirim menggunakan `Generate Example Message`.

![Mengisi Payload](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qgqkzeqmytcy5vcyj1l4.png)

Jalankan server di direktori `server/main.go` menggunakan `go run main.go` pada direktori tersebut, kemudian `Invoke` method `RegisterStudent`. Jika `Status code` adalah 0, berarti data tersebut berhasil diterima oleh server. Jika kita melihat logging dari `server/main.go`, kita akan melihat data yang dikirim dari Postman.

![Hasil Invocation](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/uhoyyh14j7lriqbbdnrl.png)

```
logging server/main.go

2023/01/27 11:04:43 magna
2023/01/27 11:04:43 -279401480
```

## Rangkuman
Pada artikel ini, kita telah belajar apa itu gRPC, Protocol Buffer, cara membuat file protobuf, mengompilasi file protobuf, mengimplementasi dalam bahasa `Go`, serta menguji menggunakan Postman. Banyak hal yang tidak dibahas di artikel ini. Penulis merekomendasikan teman-teman untuk membaca tentang [server streaming](https://grpc.io/docs/what-is-grpc/core-concepts/#server-streaming-rpc), [client streaming](https://grpc.io/docs/what-is-grpc/core-concepts/#client-streaming-rpc), [bidirectional streaming](https://grpc.io/docs/what-is-grpc/core-concepts/#bidirectional-streaming-rpc), serta cara lain [mendefinisikan message di file protobuf](https://developers.google.com/protocol-buffers/docs/proto3)

## Referensi
[https://cloud.google.com/blog/products/api-management/understanding-grpc-openapi-and-rest-and-when-to-use-them](https://cloud.google.com/blog/products/api-management/understanding-grpc-openapi-and-rest-and-when-to-use-them)
[https://developers.google.com/protocol-buffers/docs/reference/overview](https://developers.google.com/protocol-buffers/docs/reference/overview)
[https://grpc.io/docs/](https://grpc.io/docs/)