ลองสร้าง gRPC ด้วย Java แบบง่ายๆ

Phayao Boonon
4 min readDec 23, 2018

--

gRPC เป็น open source RPC ประสิทธิภาพสูงที่เริ่มพัฒนาโดย Google ที่ช่วยลด boilerplate code และช่วยเชื่อมโยง polyglot service และข้าม data center โดยใช้ protocal buffers เป็น Interface Definition Language (IDL) และรูปแบบ message interchange

gRPC client สามารถ call โดยตรงไปที่ server ในเครื่องที่แตกต่างกัน หรือถ้าเป็นเครื่องเดียวกัน ซึ่งเป็นการง่ายที่เราจะสร้าง distributed application และ service

ในบทความนี้เราจะมาสร้าง gRPC ด้วย Java แบบง่ายๆ (Hello, gRPC)

เริ่มสร้างกันเลย

ในบนความนี้จะใช้ Maven package management ดังนั้น dependency ที่ใช้จะเป็น grpc-netty, grpc-protobuf และ grpc-stub ดังนี้

<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-netty</artifactId>
<version>1.16.1</version>
</dependency>
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-protobuf</artifactId>
<version>1.16.1</version>
</dependency>
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-stub</artifactId>
<version>1.16.1</version>
</dependency>

กำหนด Service

ก่อนอื่นเราต้องกำหนด Service ด้วยการกำหนด method ที่สามารถ call remotely ได้ด้วย parameter และ return type ที่กำหนด ซึ่งสามารถกำหนดได้ในไฟล์ .proto ซึ่งใช้ protocal buffers ในการกำหนดโครงสร้างของ payload message

ในบทความนี้สร้างไฟล์ HelloService.proto สำหรับตัวอย่าง HelloService

Configuration พื้นฐาน

เริ่มต้นไฟล์ด้วนการกำหนด configuration พื้นฐานของ protocal buffers ด้วยการระบุว่าจะใช้ syntax เวอร์ชั่น proto3 และเพิ่ม java option ให้ compile แยกไฟล์ (ปรกติ java compiler จะสร้างไฟล์เดียว) และสุดท้ายก็กำหนด package ที่จะสร้าง java classes

syntax = "proto3";
option java_multiple_files = true;
package com.iphayao.grpc;

กำหนด Message structure

โดยกำหนด request payload ของ message โดยกำหนดด้วย type HelloRequest และมี attribute เป็น firstName และ lastName และกำหนด unique number ให้กับแต่ละ attribute ซึ่งจะใช้กับ protocal buffers แทนที attribute แทนที่จะใช้ชื่อ

message HelloRequest {
string fistName = 1;
string lastName = 2;
}

กำหนด response payload ของ message

message HelloResponse {
string greeting = 1;
}

กำหนด Service contract

ในไฟล์ HelloService.proto กำหนด service contract ในกับ HelloService ซึ่งเราจะใช้ hello() operation โดยจะรับ request และส่งกลับ response

service HelloService {
rpc hello(HelloRequest) returns (HelloResponse);
}

Generate Code

หลังจากที่สร้างไฟล์ proto แล้วเราก็ใช้ protocal buffers compiler ในการ generate code จากไฟล์ HelloService.proto ในบทความนี้จะใช้ maven plugin ของ protocal buffers โดยเพิ่มส่วนของ build ใน pom.xml เพื่อใช้ protobuf.maven.plugins

<build>
<extensions>
<extension>
<groupId>kr.motd.maven</groupId>
<artifactId>os-maven-plugin</artifactId>
<version>1.6.1</version>
</extension>
</extensions>
<plugins>
<plugin>
<groupId>org.xolstice.maven.plugins</groupId>
<artifactId>protobuf-maven-plugin</artifactId>
<version>0.6.1</version>
<configuration>
<protocArtifact>
com.google.protobuf:protoc:3.3.0:exe:${os.detected.classifier}
</protocArtifact>
<pluginId>grpc-java</pluginId>
<pluginArtifact>
io.grpc:protoc-gen-grpc-java:1.4.0:exe:${os.detected.classifier}
</pluginArtifact>
</configuration>
<executions>
<execution>
<goals>
<goal>compile</goal>
<goal>compile-custom</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>

ซึ่งจะต้องใช้คำสั่ง mvn build เพื่อ generate boilerplate code สำหรับ gRPC จะมีไฟล์ที่สำคัญดังนี้

  • HelloRequest — เป็นไฟล์ที่กำหนด HelloRequest type
  • HelloResponse — เป็นไฟล์ที่กำหนด HelloRespose type
  • HelloServiceGrpc — เป็นไฟล์ที่มี abstract class HelloServiceImplBase สำหรับจัดการกับ hello() operation

สร้าง Server

implementation เริ่มต้นของ HelloServiceImplBase class จะ throw runtime exception ดังนั้นเราจะต้อง implement method hello() ของเราเองเพื่อจัดการกับ hello operation ซึ่งจะ override method hello เพื่อตอบกลับด้วย HelloResponse

public class HelloServiceImpl extends HelloServiceGrpc.HelloServiceImplBase {
@Override
public void
hello(HelloRequest request, StreamObserver<HelloResponse> responseObserver) {
String greeting = new StringBuffer()
.append("Hello, ")
.append(request.getFistName())
.append(" ")
.append(request.getLastName())
.toString();

HelloResponse response = HelloResponse.newBuilder()
.setGreeting(greeting)
.build();

responseObserver.onNext(response);
responseObserver.onCompleted();
}
}

ต่อไปสร้ง gRPC Server เพื่อรอรับ request ด้วยการสร้าง Server ระบุ port ที่จะใช้ call start() method และใช้ awaitTermination() สำหรับ idle server จนกว่าจะมีการปิด server

public class GrpcServer {
public static void main(String[] args) throws IOException, InterruptedException {
int port = 8080;
Server server = ServerBuilder.forPort(port)
.addService(new HelloServiceImpl()).build();

server.start();
System.out.println("Server started listening on " + port);
server.awaitTermination();

}
}

สร้าง Client

เมื่อสร้าง server แล้วก็มาสร้าง gRPC Client เพื่อส่ง request ไปหา Server ซึ่ง gRPC จะมี channel construct ซึ่งจะมี abstract implementation เช่น connection, connection pooling, load balancing เป็นต้น

public class GrpcClient {
public static void main(String[] args) {
ManagedChannel channel = ManagedChannelBuilder.forAddress("localhost", 8080)
.usePlaintext()
.build();

HelloServiceGrpc.HelloServiceBlockingStub stub = HelloServiceGrpc.newBlockingStub(channel);

HelloResponse helloResponse = stub.hello(HelloRequest.newBuilder()
.setFistName("Demo")
.setLastName("gRPC")
.build());

System.out.println(helloResponse.getGreeting());
channel.shutdown().awaitTermination(5, TimeUnit.SECONDS);
}
}

สร้าง channel ของ gRPC ด้วย ManagedChannelBuilder โดยกำหนด host name และ port (localhost, 8080) ใช้ pain text ที่ไม่ได้เข้ารหัสใดๆ (เน้นง่ายไว้ก่อน)

ต่อไปสร้าง stub ที่จะใช้ในการ remote call ไปยัง server ซึ่ง stub เป็นวิธีหลักสำหรับ client ในการติดต่อกับ server ใช้ auto generated stub ครอบ chennel อีกที

เมื่อเราใช้ blocking/synchronous stub ซึ่ง RPC call จะรอ server response และจะไม่ return response ก็ throw exception แต่ก็สามารถใช้แบบ non-blocking/ asynchronous ได้เช่นเดียวกัน

สุดท้ายก็สร้าง call hello operation ด้วย stub ด้วยการสร้าง HelloRequest ส่งไปที่ server และรอการ response หลังจากได้ response แล้วก็แสดงผลลัพย์ทางหน้าจอ

ทดลอง Run

เมื่อเราสร้าง Serer และ Client ทั้งหมดแล้วก็มาลอง Run ดูว่าทำงานได้หรือไม่ โดยใช้คำสั่ง mvn compileเพื่อ compile project และ mvn exec:java เพื่อรัน server และ client ของ gRPC

Run Server

$ mvn exec:java -Dexec.mainClass=com.iphayao.grpc.server.GrpcServer
$ Server started listening on 8080

Run Client

$ mvn exec:java -Dexec.mainClass=com.iphayao.grpc.client.GrpcClient
$ Hello, Demo gRPC

สรุป

บทความนี้ทดลองสร้าง gRPC project อย่างง่ายๆ ด้วย Java และ maven จะเห็นได้ว่าเราสามารถสร้างได้ไม่ยากโดยใช้ dependency ที่จำเป็นของ gRPC เพื่อจัดการทั้ง server และ clinet แต่ gRPC สามารถรองรับได้หลายภาษาซึ่งเหมาะสมมากกับแนวคิด microservice ที่แต่ละ service เป็นอิสระทาง framework ต่อกันแต่ก็ใช้ gRPC ในการสื่อสารกันได้

--

--

Phayao Boonon
Phayao Boonon

Written by Phayao Boonon

Software Engineer 👨🏻‍💻 Stay Hungry Stay Foolish

No responses yet