1. บทนำ
ในโค้ดแล็บนี้ คุณจะได้ใช้ gRPC-Java เพื่อสร้างไคลเอ็นต์และเซิร์ฟเวอร์ซึ่งเป็นรากฐานของแอปพลิเคชันการแมปเส้นทางที่เขียนด้วย Java
เมื่อจบบทแนะนำนี้ คุณจะมีไคลเอ็นต์ที่เชื่อมต่อกับเซิร์ฟเวอร์ระยะไกลโดยใช้ gRPC เพื่อรับชื่อหรือที่อยู่ไปรษณีย์ของสิ่งที่อยู่ ณ พิกัดที่เฉพาะเจาะจงบนแผนที่ แอปพลิเคชันที่สมบูรณ์อาจใช้การออกแบบไคลเอ็นต์-เซิร์ฟเวอร์นี้เพื่อแจงนับหรือสรุปจุดที่น่าสนใจตามเส้นทาง
API ของเซิร์ฟเวอร์จะกำหนดไว้ในไฟล์ Protocol Buffers ซึ่งจะใช้เพื่อสร้างโค้ดมาตรฐานสำหรับไคลเอ็นต์และเซิร์ฟเวอร์เพื่อให้สื่อสารกันได้ ซึ่งจะช่วยประหยัดเวลาและแรงในการติดตั้งใช้งานฟังก์ชันดังกล่าว
โค้ดที่สร้างขึ้นนี้ไม่เพียงแต่จัดการความซับซ้อนของการสื่อสารระหว่างเซิร์ฟเวอร์และไคลเอ็นต์เท่านั้น แต่ยังจัดการการซีเรียลไลซ์และการดีซีเรียลไลซ์ข้อมูลด้วย
สิ่งที่คุณจะได้เรียนรู้
- วิธีใช้ Protocol Buffers เพื่อกำหนด API ของบริการ
- วิธีสร้างไคลเอ็นต์และเซิร์ฟเวอร์ที่ใช้ gRPC จากคำจำกัดความของ Protocol Buffers โดยใช้การสร้างโค้ดอัตโนมัติ
- ความเข้าใจเกี่ยวกับการสื่อสารระหว่างไคลเอ็นต์กับเซิร์ฟเวอร์ด้วย gRPC
Codelab นี้มีไว้สำหรับนักพัฒนาแอป Java ที่เพิ่งเริ่มใช้ gRPC หรือต้องการทบทวน gRPC หรือใครก็ตามที่สนใจสร้างระบบแบบกระจาย ไม่จำเป็นต้องมีประสบการณ์เกี่ยวกับ gRPC มาก่อน
2. ก่อนเริ่มต้น
ข้อกำหนดเบื้องต้น
- JDK เวอร์ชัน 8 ขึ้นไป
รับโค้ด
Codelab นี้มีโครงสร้างของซอร์สโค้ดของแอปพลิเคชันเพื่อให้คุณทำต่อได้ คุณจึงไม่ต้องเริ่มต้นจากศูนย์ ขั้นตอนต่อไปนี้จะแสดงวิธีส่งแอปพลิเคชันให้เสร็จสมบูรณ์ ซึ่งรวมถึงการใช้ปลั๊กอินคอมไพเลอร์ Protocol Buffer เพื่อสร้างโค้ด gRPC ที่ซ้ำกัน
ก่อนอื่น ให้สร้างไดเรกทอรีการทำงานของ Codelab แล้วใช้คำสั่ง cd เพื่อเข้าไปในไดเรกทอรีดังกล่าว
mkdir grpc-java-getting-started && cd grpc-java-getting-started
ดาวน์โหลดและแตกไฟล์ Codelab โดยทำดังนี้
curl -sL https://github.com/grpc-ecosystem/grpc-codelabs/archive/refs/heads/v1.tar.gz \
| tar xvz --strip-components=4 \
grpc-codelabs-1/codelabs/grpc-java-getting-started/start_here
หรือคุณจะดาวน์โหลดไฟล์ .zip ที่มีเฉพาะไดเรกทอรี Codelab แล้วแตกไฟล์ด้วยตนเองก็ได้
ซอร์สโค้ดที่เสร็จสมบูรณ์แล้วพร้อมใช้งานใน GitHub หากคุณไม่ต้องการพิมพ์การติดตั้งใช้งาน
3. กำหนดบริการ
ขั้นตอนแรกคือการกำหนดบริการ gRPC ของแอปพลิเคชัน เมธอด RPC รวมถึงประเภทข้อความคำขอและการตอบกลับโดยใช้ Protocol Buffers บริการของคุณจะให้ข้อมูลต่อไปนี้
- เมธอด RPC ที่ชื่อ
GetFeature
ซึ่งเซิร์ฟเวอร์นำมาใช้และไคลเอ็นต์เรียกใช้ - ประเภทข้อความ
Point
และFeature
ซึ่งเป็นโครงสร้างข้อมูลที่แลกเปลี่ยนระหว่างไคลเอ็นต์กับเซิร์ฟเวอร์เมื่อใช้เมธอดGetFeature
ไคลเอ็นต์จะระบุพิกัดแผนที่เป็นPoint
ในคำขอGetFeature
ไปยังเซิร์ฟเวอร์ และเซิร์ฟเวอร์จะตอบกลับด้วยFeature
ที่เกี่ยวข้องซึ่งอธิบายสิ่งที่อยู่ในพิกัดเหล่านั้น
วิธีการ RPC นี้และประเภทข้อความของวิธีการนี้จะกำหนดไว้ในไฟล์ src/main/proto/routeguide/route_guide.proto
ของซอร์สโค้ดที่ระบุ
Protocol Buffers เรียกกันโดยทั่วไปว่า protobuf ดูข้อมูลเพิ่มเติมเกี่ยวกับคำศัพท์ gRPC ได้ที่แนวคิดหลัก สถาปัตยกรรม และวงจรของ gRPC
เนื่องจากเราจะสร้างโค้ด Java ในตัวอย่างนี้ เราจึงระบุตัวเลือกไฟล์ java_package
และชื่อสำหรับคลาส Java ใน .proto
ดังนี้
option java_package = "io.grpc.examples.routeguide";
option java_outer_classname = "RouteGuideProto";
ประเภทข้อความ
ในrouteguide/route_guide.proto
ไฟล์ของซอร์สโค้ด ให้กำหนดPoint
ประเภทข้อความก่อน Point
แสดงคู่พิกัดละติจูดและลองจิจูดบนแผนที่ สำหรับ Codelab นี้ ให้ใช้จำนวนเต็มสำหรับพิกัด
message Point {
int32 latitude = 1;
int32 longitude = 2;
}
หมายเลข 1
และ 2
เป็นหมายเลขรหัสที่ไม่ซ้ำกันสำหรับแต่ละฟิลด์ในโครงสร้าง message
จากนั้นกำหนดFeature
ประเภทข้อความ Feature
ใช้ฟิลด์ string
สำหรับชื่อหรือที่อยู่ไปรษณีย์ของสิ่งหนึ่งๆ ในสถานที่ที่ระบุโดย Point
ดังนี้
message Feature {
// The name or address of the feature.
string name = 1;
// The point where the feature is located.
Point location = 2;
}
วิธีการบริการ
ไฟล์ route_guide.proto
มีโครงสร้าง service
ชื่อ RouteGuide
ซึ่งกำหนดวิธีการอย่างน้อย 1 วิธีที่บริการของแอปพลิเคชันมีให้
เพิ่มrpc
method GetFeature
ภายในคำจำกัดความของ RouteGuide
ดังที่อธิบายไว้ก่อนหน้านี้ วิธีนี้จะค้นหาชื่อหรือที่อยู่ของสถานที่จากชุดพิกัดที่กำหนด ดังนั้นให้ GetFeature
แสดง Feature
สำหรับ Point
ที่กำหนด
service RouteGuide {
// Definition of the service goes here
// Obtains the feature at a given position.
rpc GetFeature(Point) returns (Feature) {}
}
นี่คือเมธอด RPC แบบเอกภาค ซึ่งเป็น RPC อย่างง่ายที่ไคลเอ็นต์ส่งคำขอไปยังเซิร์ฟเวอร์และรอให้เซิร์ฟเวอร์ส่งการตอบกลับกลับมา เหมือนกับการเรียกฟังก์ชันในเครื่อง
4. สร้างโค้ดไคลเอ็นต์และเซิร์ฟเวอร์
จากนั้นเราต้องสร้างอินเทอร์เฟซไคลเอ็นต์และเซิร์ฟเวอร์ gRPC จาก.proto
คำจำกัดความของบริการ เราดำเนินการนี้โดยใช้คอมไพเลอร์บัฟเฟอร์โปรโตคอล protoc
ร่วมกับปลั๊กอิน gRPC Java พิเศษ คุณต้องใช้คอมไพเลอร์ proto3 (ซึ่งรองรับทั้งไวยากรณ์ proto2 และ proto3) เพื่อสร้างบริการ gRPC
เมื่อใช้ Gradle หรือ Maven protoc
ปลั๊กอินการสร้างจะสร้างโค้ดที่จำเป็นเป็นส่วนหนึ่งของการสร้างได้ คุณดูวิธีสร้างโค้ดจากไฟล์ .proto
ของตัวเองได้ใน README ของ grpc-java
เราได้จัดเตรียมสภาพแวดล้อมและการกำหนดค่า Gradle ไว้ในซอร์สโค้ดของโค้ดแล็บเพื่อสร้างโปรเจ็กต์นี้
เรียกใช้คำสั่งต่อไปนี้ในไดเรกทอรี grpc-java-getting-started
$ chmod +x gradlew $ ./gradlew generateProto
ระบบจะสร้างคลาสต่อไปนี้จากคำจำกัดความของบริการ
Feature.java
,Point.java
และอื่นๆ ที่มีโค้ด Protocol Buffer ทั้งหมดเพื่อสร้างข้อมูล จัดรูปแบบ และดึงข้อมูลประเภทข้อความคำขอและการตอบกลับRouteGuideGrpc.java
ซึ่งมี (พร้อมกับโค้ดอื่นๆ ที่มีประโยชน์) คลาสพื้นฐานสำหรับRouteGuide
เซิร์ฟเวอร์ที่จะใช้RouteGuideGrpc.RouteGuideImplBase
โดยมีเมธอดทั้งหมดที่กำหนดไว้ในRouteGuide
คลาสบริการและคลาส Stub สำหรับไคลเอ็นต์ที่จะใช้
5. ติดตั้งใช้งานเซิร์ฟเวอร์
ก่อนอื่นมาดูวิธีสร้างRouteGuide
เซิร์ฟเวอร์กัน การทำให้RouteGuide
บริการของเราทำงานได้ตามที่ควรจะเป็นมี 2 ส่วน ดังนี้
- การใช้ส่วนติดต่อบริการที่สร้างขึ้นจากคำจำกัดความของบริการ ซึ่งจะ "ทำงาน" จริงของบริการ
- เรียกใช้เซิร์ฟเวอร์ gRPC เพื่อรอรับคำขอจากไคลเอ็นต์และส่งคำขอไปยังการติดตั้งใช้งานบริการที่ถูกต้อง
ใช้ RouteGuide
ดังที่เห็น เซิร์ฟเวอร์ของเรามีคลาส RouteGuideService
ที่ขยายคลาสแอบสแทร็กต์ RouteGuideGrpc.RouteGuideImplBase
ที่สร้างขึ้น
private static class RouteGuideService extends RouteGuideGrpc.RouteGuideImplBase {
...
}
เราได้จัดเตรียมไฟล์ 2 ไฟล์ต่อไปนี้เพื่อเริ่มต้นเซิร์ฟเวอร์ด้วยฟีเจอร์ต่างๆ
./src/main/java/io/grpc/examples/routeguide/RouteGuideUtil.java
./src/main/resources/io/grpc/examples/routeguide/route_guide_db.json
มาดูรายละเอียดการติดตั้งใช้งาน RPC อย่างง่ายกัน
RPC แบบ Unary
RouteGuideService
ใช้ทุกวิธีการบริการของเรา ในกรณีนี้คือ GetFeature()
ซึ่งรับข้อความ Point
จากไคลเอ็นต์และส่งคืนข้อมูลสถานที่ที่เกี่ยวข้องจากรายการสถานที่ที่รู้จักในข้อความ Feature
@Override
public void getFeature(Point request, StreamObserver<Feature> responseObserver) {
responseObserver.onNext(checkFeature(request));
responseObserver.onCompleted();
}
เมธอด getFeature()
ใช้พารามิเตอร์ 2 รายการ ดังนี้
Point
: คำขอStreamObserver<Feature>
: เครื่องมือสังเกตการตอบกลับ ซึ่งเป็นอินเทอร์เฟซพิเศษสำหรับเซิร์ฟเวอร์ในการเรียกด้วยการตอบกลับ
หากต้องการส่งคำตอบกลับไปยังไคลเอ็นต์และสิ้นสุดการเรียกใช้ ให้ทำดังนี้
- เราสร้างและป้อนข้อมูล
Feature
ออบเจ็กต์การตอบกลับเพื่อส่งคืนไปยังไคลเอ็นต์ตามที่ระบุไว้ในคำจำกัดความของบริการ ในตัวอย่างนี้ เราจะทำเช่นนี้ในcheckFeature()
เมธอดส่วนตัวแยกต่างหาก - เราใช้วิธี
onNext()
ของเครื่องสังเกตการตอบกลับเพื่อแสดงผลFeature
- เราใช้เมธอด
onCompleted()
ของเครื่องสังเกตการตอบกลับเพื่อระบุว่าเราจัดการกับ RPC เสร็จแล้ว
เริ่มเซิร์ฟเวอร์
เมื่อเราได้ใช้เมธอดบริการทั้งหมดแล้ว เราต้องเริ่มเซิร์ฟเวอร์ gRPC เพื่อให้ไคลเอ็นต์ใช้บริการของเราได้จริง เราจะรวมการสร้างออบเจ็กต์ ServerBuilder ไว้ในบอยเลอร์เพลตของเรา
ServerBuilder.forPort(port), port, RouteGuideUtil.parseFeatures(featureFile)
เราสร้างบริการในตัวสร้างดังนี้
- ระบุพอร์ตที่เราต้องการใช้เพื่อรอคำขอของไคลเอ็นต์โดยใช้เมธอด
forPort()
ของ Builder (จะใช้ที่อยู่ไวลด์การ์ด) - สร้างอินสแตนซ์ของคลาสการติดตั้งใช้งานบริการ
RouteGuideService
และส่งไปยังเมธอดaddService()
ของ Builder - เรียกใช้
build()
ในบิลเดอร์เพื่อสร้างเซิร์ฟเวอร์ RPC สำหรับบริการของเรา
ข้อมูลโค้ดต่อไปนี้แสดงวิธีสร้างออบเจ็กต์ ServerBuilder
/** Create a RouteGuide server listening on {@code port} using {@code featureFile} database. */
public RouteGuideServer(int port, URL featureFile) throws IOException {
this(Grpc.newServerBuilderForPort(port, InsecureServerCredentials.create()),
port, RouteGuideUtil.parseFeatures(featureFile));
}
ข้อมูลโค้ดต่อไปนี้แสดงวิธีสร้างออบเจ็กต์เซิร์ฟเวอร์สำหรับบริการ RouteGuide
/** Create a RouteGuide server using serverBuilder as a base and features as data. */
public RouteGuideServer(ServerBuilder<?> serverBuilder, int port, Collection<Feature> features) {
this.port = port;
server = serverBuilder.addService(new RouteGuideService(features))
.build();
}
ใช้เมธอดเริ่มต้นที่เรียก start
ในเซิร์ฟเวอร์ที่เราสร้างไว้ข้างต้น
public void start() throws IOException {
server.start();
logger.info("Server started, listening on " + port);
}
ใช้วิธีรอให้เซิร์ฟเวอร์ทำงานเสร็จเพื่อไม่ให้ออกจากระบบทันที
/** Await termination on the main thread since the grpc library uses daemon threads. */
private void blockUntilShutdown() throws InterruptedException {
if (server != null) {
server.awaitTermination();
}
}
ดังที่เห็น เราสร้างและเริ่มเซิร์ฟเวอร์โดยใช้ ServerBuilder
ในวิธีหลัก เราจะดำเนินการต่อไปนี้
- สร้างอินสแตนซ์
RouteGuideServer
- โทรหา
start()
เพื่อเปิดใช้งานเซิร์ฟเวอร์ RPC สำหรับบริการของเรา - รอให้ระบบหยุดบริการโดยการโทรหา
blockUntilShutdown()
public static void main(String[] args) throws Exception {
RouteGuideServer server = new RouteGuideServer(8980);
server.start();
server.blockUntilShutdown();
}
6. สร้างไคลเอ็นต์
ในส่วนนี้ เราจะมาดูการสร้างไคลเอ็นต์สำหรับบริการ RouteGuide
สร้างอินสแตนซ์ของ Stub
หากต้องการเรียกใช้เมธอดของบริการ เราต้องสร้างสตับก่อน มี Stub อยู่ 2 ประเภท แต่เราต้องใช้เฉพาะ Stub ที่บล็อกสำหรับ Codelab นี้ โดยมี 2 ประเภทดังนี้
- สแต็บการบล็อก/ซิงโครนัสที่ทำการเรียก RPC และรอให้เซิร์ฟเวอร์ตอบกลับ จากนั้นจะส่งการตอบกลับหรือยกเว้น
- สตับที่ไม่บล็อก/แบบอะซิงโครนัสที่ทำการเรียกที่ไม่บล็อกไปยังเซิร์ฟเวอร์ ซึ่งจะส่งคืนการตอบกลับแบบอะซิงโครนัส คุณจะโทรแบบสตรีมมิงบางประเภทได้โดยใช้ Stub แบบไม่พร้อมกันเท่านั้น
ก่อนอื่นเราต้องสร้างแชแนล gRPC แล้วใช้แชแนลนั้นเพื่อสร้าง Stub
เราใช้ ManagedChannelBuilder
เพื่อสร้างช่องได้โดยตรง
ManagedChannelBuilder.forAddress(host, port).usePlaintext().build
แต่เราจะใช้วิธีการยูทิลิตีที่รับสตริงที่มี hostname:port
Grpc.newChannelBuilder(target, InsecureChannelCredentials.create()).build();
ตอนนี้เราสามารถใช้ช่องเพื่อสร้าง Stub การบล็อกได้แล้ว สำหรับ Codelab นี้ เรามีเฉพาะ RPC ที่บล็อก ดังนั้นเราจึงใช้เมธอด newBlockingStub
ที่ระบุไว้ในคลาส RouteGuideGrpc
ที่เราสร้างจาก .proto
blockingStub = RouteGuideGrpc.newBlockingStub(channel);
วิธีการเรียกใช้บริการ
ตอนนี้มาดูวิธีเรียกใช้เมธอดบริการกัน
RPC แบบง่าย
การเรียกใช้ RPC อย่างง่าย GetFeature
นั้นแทบจะตรงไปตรงมาเหมือนกับการเรียกใช้เมธอดในเครื่อง
เราสร้างและป้อนข้อมูลออบเจ็กต์บัฟเฟอร์โปรโตคอลคำขอ (ในกรณีของเราคือ Point
) ส่งไปยังเมธอด getFeature()
ใน Blocking Stub และรับ Feature
กลับมา
หากเกิดข้อผิดพลาด ระบบจะเข้ารหัสเป็น Status
ซึ่งเราจะได้รับจาก StatusRuntimeException
Point request = Point.newBuilder().setLatitude(lat).setLongitude(lon).build();
Feature feature;
try {
feature = blockingStub.getFeature(request);
} catch (StatusRuntimeException e) {
logger.log(Level.WARNING, "RPC failed: {0}", e.getStatus());
return;
}
บอยเลอร์เพลตจะบันทึกข้อความที่มีเนื้อหาตามว่ามีฟีเจอร์ ณ จุดที่ระบุหรือไม่
7. ลองใช้เลย!
- เรียกใช้คำสั่งต่อไปนี้ในไดเรกทอรี
start_here
$ ./gradlew installDist
ซึ่งจะคอมไพล์โค้ด แพ็กเกจในไฟล์ JAR และสร้างสคริปต์ที่เรียกใช้ตัวอย่าง ระบบจะสร้างไฟล์เหล่านี้ในไดเรกทอรี build/install/start_here/bin/
สคริปต์คือ route-guide-server
และ route-guide-client
เซิร์ฟเวอร์ต้องทำงานก่อนจึงจะเริ่มไคลเอ็นต์ได้
- เรียกใช้เซิร์ฟเวอร์
$ ./build/install/start_here/bin/route-guide-server
- เรียกใช้ไคลเอ็นต์
$ ./build/install/start_here/bin/route-guide-client
คุณจะเห็นเอาต์พุตลักษณะนี้ โดยระบบจะละเว้นการประทับเวลาเพื่อความชัดเจน
INFO: *** GetFeature: lat=409,146,138 lon=-746,188,906 INFO: Found feature called "Berkshire Valley Management Area Trail, Jefferson, NJ, USA" at 40.915, -74.619 INFO: *** GetFeature: lat=0 lon=0 INFO: Found no feature at 0, 0
8. ขั้นตอนถัดไป
- ดูวิธีการทำงานของ gRPC ในข้อมูลเบื้องต้นเกี่ยวกับ gRPC และแนวคิดหลัก
- ดูบทแนะนำเกี่ยวกับพื้นฐาน
- สำรวจข้อมูลอ้างอิงของ API