1. บทนำ
ใน Codelab นี้ คุณจะได้ใช้ gRPC เพื่อสร้างไคลเอ็นต์และเซิร์ฟเวอร์ที่เป็นรากฐานของแอปพลิเคชันการกำหนดเส้นทางที่เขียนด้วย Java
เมื่อจบบทแนะนำนี้ คุณจะมีแอปพลิเคชัน gRPC HelloWorld อย่างง่ายที่ติดตั้งปลั๊กอิน gRPC OpenTelemetry และสามารถดูเมตริกความสามารถในการสังเกตที่ส่งออกใน Prometheus ได้
สิ่งที่คุณจะได้เรียนรู้
- วิธีตั้งค่าปลั๊กอิน OpenTelemetry สำหรับแอปพลิเคชัน gRPC Java ที่มีอยู่
- การเรียกใช้อินสแตนซ์ Prometheus ในเครื่อง
- การส่งออกเมตริกไปยัง Prometheus
- ดูเมตริกจากแดชบอร์ด Prometheus
2. ก่อนเริ่มต้น
สิ่งที่คุณต้องมี
gitcurlJDKเวอร์ชัน 8 ขึ้นไป
ติดตั้งข้อกำหนดเบื้องต้นดังนี้
sudo apt-get update -y
sudo apt-get upgrade -y
sudo apt-get install -y git curl
รับโค้ด
Codelab นี้มีโครงสร้างโค้ดที่สร้างไว้ล่วงหน้าเพื่อช่วยให้คุณเริ่มต้นใช้งานได้ง่ายขึ้น ขั้นตอนต่อไปนี้จะแนะนำคุณในการติดตั้งปลั๊กอิน gRPC OpenTelemetry ในแอปพลิเคชัน
ซอร์สโค้ดโครงสร้างสำหรับ Codelab นี้อยู่ในไดเรกทอรี github นี้ directory หากไม่ต้องการเขียนโค้ดด้วยตนเอง คุณสามารถดูซอร์สโค้ดที่เสร็จสมบูรณ์แล้วได้ในไดเรกทอรี completed
ขั้นแรก ให้โคลนที่เก็บ gRPC Codelab แล้วเปลี่ยนไดเรกทอรีเป็นโฟลเดอร์ grpc-java-opentelemetry
git clone -b v1 https://github.com/grpc-ecosystem/grpc-codelabs.git
cd grpc-codelabs/codelabs/grpc-java-opentelemetry/
หรือจะดาวน์โหลดไฟล์ .zip ที่มีเฉพาะไดเรกทอรี Codelab แล้วแตกไฟล์ด้วยตนเองก็ได้
3. ลงทะเบียนปลั๊กอิน OpenTelemetry
เราต้องมีแอปพลิเคชัน gRPC เพื่อเพิ่มปลั๊กอิน gRPC OpenTelemetry ใน Codelab นี้ เราจะใช้ไคลเอ็นต์และเซิร์ฟเวอร์ gRPC HelloWorld อย่างง่ายที่เราจะติดตั้งปลั๊กอิน gRPC OpenTelemetry
ขั้นตอนแรกคือการลงทะเบียนปลั๊กอิน OpenTelemetry ที่กำหนดค่าด้วยเครื่องมือส่งออก Prometheus ในไคลเอ็นต์ เปิด codelabs/grpc-java-opentelemetry/start_here/src/main/java/io/grpc/codelabs/opentelemetry/OpenTelemetryClient.java ด้วยโปรแกรมแก้ไขที่คุณชื่นชอบ จากนั้นแก้ไข main เพื่อเพิ่มโค้ดสำหรับการตั้งค่า gRPC Java OpenTelemetry API
ตั้งค่าการติดตั้งในไคลเอ็นต์
สร้างเครื่องมือส่งออก Prometheus
สร้าง PrometheusHttpServer เพื่อแปลงเมตริก OpenTelemetry เป็นรูปแบบ Prometheus และแสดงเมตริกเหล่านี้ผ่าน HttpServer ข้อมูลโค้ดต่อไปนี้จะสร้าง เครื่องมือส่งออก Prometheus ใหม่
// Default prometheus port i.e `prometheusPort` has been initialized to 9465
PrometheusHttpServer prometheusExporter = PrometheusHttpServer.builder()
.setPort(prometheusPort)
.build();
สร้างอินสแตนซ์ OpenTelemetry SDK
ลงทะเบียนที่สร้างไว้ข้างต้น prometheusExporter เป็น MetricReader เพื่ออ่านเมตริกจาก SdkMeterProvider SdkMeterProvider ใช้เพื่อกำหนดค่าการตั้งค่าเมตริก
SdkMeterProvider sdkMeterProvider = SdkMeterProvider.builder()
.registerMetricReader(prometheusExporter)
.build();
สร้างอินสแตนซ์ของ OpenTelemetrySdk ด้วย sdkMeterProvider ที่สร้างไว้ข้างต้นสำหรับการใช้งาน SDK ของ OpenTelemetry
OpenTelemetrySdk openTelemetrySdk =OpenTelemetrySdk.builder()
.setMeterProvider(sdkMeterProvider)
.build();
สร้างอินสแตนซ์ GrpcOpenTelemetry
ใช้ GrpcOpenTelemetry API เพื่อตั้งค่า OpenTelemetry SDK ที่ใช้เครื่องมือส่งออกเมตริก Prometheus
GrpcOpenTelemetry grpcOpenTelmetry = GrpcOpenTelemetry.newBuilder()
.sdk(openTelemetrySdk)
.build();
// Registers gRPC OpenTelemetry globally.
grpcOpenTelmetry.registerGlobal();
เมื่อลงทะเบียนอินสแตนซ์ GrpcOpenTelemetry ทั่วโลกโดยใช้ registerGlobal แล้ว ไคลเอ็นต์และเซิร์ฟเวอร์ gRPC ที่สร้างขึ้นหลังจากนั้นทั้งหมดจะได้รับการติดตั้ง OpenTelemetry
ปิด OpenTelemetry SDK
การปิดใช้งานต้องเกิดขึ้นภายใน ShutDownHook openTelemetrySdk.close() จะปิด SDK และเรียกใช้การปิดใน SdkMeterProvider ด้วย
ตั้งค่าการติดตั้งในเซิร์ฟเวอร์
ในทำนองเดียวกัน ให้เพิ่ม GrpcOpenTelemetry ลงในเซิร์ฟเวอร์ด้วย เปิด codelabs/grpc-java-opentelemetry/start_here/src/main/java/io/grpc/codelabs/opentelemetry/OpenTelemetryServer.java แล้วเพิ่มโค้ดเพื่อเริ่มต้น GrpcOpenTelemetry
สร้างเครื่องมือส่งออก Prometheus
เนื่องจาก Codelab นี้อาจทำงานจากเครื่องเดียวกัน เราจึงใช้พอร์ตอื่นเพื่อโฮสต์เมตริกฝั่งเซิร์ฟเวอร์ gRPC เพื่อหลีกเลี่ยงความขัดแย้งของพอร์ตขณะสร้าง PrometheusHttpServer
// Default prometheus port i.e `prometheusPort` has been set to 9464
PrometheusHttpServer prometheusExporter = PrometheusHttpServer.builder()
.setPort(prometheusPort)
.build();
สร้างอินสแตนซ์ OpenTelemetry SDK
SdkMeterProvider sdkMeterProvider = SdkMeterProvider.builder()
.registerMetricReader(prometheusExporter)
.build();
เริ่มต้น GrpcOpenTelemetry ด้วย OpenTelemetry SDK
OpenTelemetrySdk openTelemetrySdk =OpenTelemetrySdk.builder()
.setMeterProvider(sdkMeterProvider)
.build();
สร้างอินสแตนซ์ GrpcOpenTelemetry
GrpcOpenTelemetry grpcOpenTelmetry = GrpcOpenTelemetry.newBuilder()
.sdk(openTelemetrySdk)
.build();
// Registers gRPC OpenTelemetry globally.
grpcOpenTelmetry.registerGlobal();
ปิด OpenTelemetry SDK
หลังจากปิดแชแนล gRPC แล้ว การเรียกใช้ openTelemetrySdk.close() จะปิด SDK และเรียกใช้การปิดใน SdkMeterProvider ด้วย
4. การเรียกใช้ตัวอย่างและการดูเมตริก
หากต้องการเรียกใช้เซิร์ฟเวอร์ ให้เรียกใช้คำสั่งต่อไปนี้
cd start_here
../gradlew installDist
./build/install/start_here/bin/opentelemetry-server
หากตั้งค่าสำเร็จ คุณจะเห็นเอาต์พุตต่อไปนี้สำหรับเซิร์ฟเวอร์
[date and time] io.grpc.codelabs.opentelemetry.OpenTelemetryServer start
INFO: Server started, listening on 50051
ขณะที่เซิร์ฟเวอร์กำลังทำงาน ให้เรียกใช้ไคลเอ็นต์ในเทอร์มินัลอื่นด้วยคำสั่งต่อไปนี้
./build/install/start_here/bin/opentelemetry-client world
การเรียกใช้ที่สำเร็จจะมีลักษณะดังนี้
[date and time]io.grpc.codelabs.opentelemetry.OpenTelemetryClient greet
INFO: Greeting: Hello world
[date and time] io.grpc.codelabs.opentelemetry.OpenTelemetryClient greet
INFO: Will try to greet world ...
[date and time]io.grpc.codelabs.opentelemetry.OpenTelemetryClient greet
INFO: Greeting: Hello world
เนื่องจากเราได้ตั้งค่าปลั๊กอิน gRPC OpenTelemetry เพื่อส่งออกเมตริกโดยใช้ Prometheus เมตริกเหล่านั้นจะพร้อมใช้งานใน localhost:9464 สำหรับเซิร์ฟเวอร์ และ localhost:9465 สำหรับไคลเอ็นต์
หากต้องการดูเมตริกไคลเอ็นต์ ให้เรียกใช้คำสั่งต่อไปนี้
curl localhost:9465/metrics
ผลลัพธ์จะมีรูปแบบดังนี้
# HELP grpc_client_attempt_duration_seconds Time taken to complete a client call attempt
# TYPE grpc_client_attempt_duration_seconds histogram
grpc_client_attempt_duration_seconds_bucket{grpc_method="helloworld.Greeter/SayHello",grpc_status="OK",grpc_target="dns:///localhost:50051",otel_scope_name="grpc-java",otel_scope_version="1.66.0",le="0.002"} 0
grpc_client_attempt_duration_seconds_bucket{grpc_method="helloworld.Greeter/SayHello",grpc_status="OK",grpc_target="dns:///localhost:50051",otel_scope_name="grpc-java",otel_scope_version="1.66.0",le="0.003"} 2
grpc_client_attempt_duration_seconds_bucket{grpc_method="helloworld.Greeter/SayHello",grpc_status="OK",grpc_target="dns:///localhost:50051",otel_scope_name="grpc-java",otel_scope_version="1.66.0",le="0.004"} 14
grpc_client_attempt_duration_seconds_bucket{grpc_method="helloworld.Greeter/SayHello",grpc_status="OK",grpc_target="dns:///localhost:50051",otel_scope_name="grpc-java",otel_scope_version="1.66.0",le="0.005"} 29
grpc_client_attempt_duration_seconds_bucket{grpc_method="helloworld.Greeter/SayHello",grpc_status="OK",grpc_target="dns:///localhost:50051",otel_scope_name="grpc-java",otel_scope_version="1.66.0",le="0.1"} 33
grpc_client_attempt_duration_seconds_bucket{grpc_method="helloworld.Greeter/SayHello",grpc_status="OK",grpc_target="dns:///localhost:50051",otel_scope_name="grpc-java",otel_scope_version="1.66.0",le="+Inf"} 34
grpc_client_attempt_duration_seconds_count{grpc_method="helloworld.Greeter/SayHello",grpc_status="OK",grpc_target="dns:///localhost:50051",otel_scope_name="grpc-java",otel_scope_version="1.66.0"} 34
grpc_client_attempt_duration_seconds_sum{grpc_method="helloworld.Greeter/SayHello",grpc_status="OK",grpc_target="dns:///localhost:50051",otel_scope_name="grpc-java",otel_scope_version="1.66.0"} 0.46512665300000006
# HELP grpc_client_attempt_rcvd_total_compressed_message_size_bytes Compressed message bytes received per call attempt
# TYPE grpc_client_attempt_rcvd_total_compressed_message_size_bytes histogram
grpc_client_attempt_rcvd_total_compressed_message_size_bytes_bucket{grpc_method="helloworld.Greeter/SayHello",grpc_status="OK",grpc_target="dns:///localhost:50051",otel_scope_name="grpc-java",otel_scope_version="1.66.0",le="0.0"} 0
grpc_client_attempt_rcvd_total_compressed_message_size_bytes_sum{grpc_method="helloworld.Greeter/SayHello",grpc_status="OK",grpc_target="dns:///localhost:50051",otel_scope_name="grpc-java",otel_scope_version="1.66.0"} 442.0
# HELP grpc_client_attempt_sent_total_compressed_message_size_bytes Compressed message bytes sent per client call attempt
# TYPE grpc_client_attempt_sent_total_compressed_message_size_bytes histogram
grpc_client_attempt_sent_total_compressed_message_size_bytes_bucket{grpc_method="helloworld.Greeter/SayHello",grpc_status="OK",grpc_target="dns:///localhost:50051",otel_scope_name="grpc-java",otel_scope_version="1.66.0",le="0.0"} 0
grpc_client_attempt_sent_total_compressed_message_size_bytes_bucket{grpc_method="helloworld.Greeter/SayHello",grpc_status="OK",grpc_target="dns:///localhost:50051",otel_scope_name="grpc-java",otel_scope_version="1.66.0",le="1024.0"} 34
grpc_client_attempt_sent_total_compressed_message_size_bytes_sum{grpc_method="helloworld.Greeter/SayHello",grpc_status="OK",grpc_target="dns:///localhost:50051",otel_scope_name="grpc-java",otel_scope_version="1.66.0"} 238.0
# HELP grpc_client_attempt_started_total Number of client call attempts started
# TYPE grpc_client_attempt_started_total counter
grpc_client_attempt_started_total{grpc_method="helloworld.Greeter/SayHello",grpc_target="dns:///localhost:50051",otel_scope_name="grpc-java",otel_scope_version="1.66.0"} 34.0
# HELP grpc_client_call_duration_seconds Time taken by gRPC to complete an RPC from application's perspective
# TYPE grpc_client_call_duration_seconds histogram
grpc_client_call_duration_seconds_bucket{grpc_method="helloworld.Greeter/SayHello",grpc_status="OK",grpc_target="dns:///localhost:50051",otel_scope_name="grpc-java",otel_scope_version="1.66.0",le="0.0"} 0
grpc_client_call_duration_seconds_bucket{grpc_method="helloworld.Greeter/SayHello",grpc_status="OK",grpc_target="dns:///localhost:50051",otel_scope_name="grpc-java",otel_scope_version="1.66.0",le="0.003"} 2
grpc_client_call_duration_seconds_bucket{grpc_method="helloworld.Greeter/SayHello",grpc_status="OK",grpc_target="dns:///localhost:50051",otel_scope_name="grpc-java",otel_scope_version="1.66.0",le="+Inf"} 34
grpc_client_call_duration_seconds_count{grpc_method="helloworld.Greeter/SayHello",grpc_status="OK",grpc_target="dns:///localhost:50051",otel_scope_name="grpc-java",otel_scope_version="1.66.0"} 34
grpc_client_call_duration_seconds_sum{grpc_method="helloworld.Greeter/SayHello",grpc_status="OK",grpc_target="dns:///localhost:50051",otel_scope_name="grpc-java",otel_scope_version="1.66.0"} 0.512708707
# TYPE target_info gauge
target_info{service_name="unknown_service:java",telemetry_sdk_language="java",telemetry_sdk_name="opentelemetry",telemetry_sdk_version="1.40.0"} 1
ในทำนองเดียวกัน สำหรับเมตริกฝั่งเซิร์ฟเวอร์ ให้เรียกใช้คำสั่งต่อไปนี้
curl localhost:9464/metrics
5. การดูเมตริกใน Prometheus
ในส่วนนี้ เราจะตั้งค่าอินสแตนซ์ Prometheus ที่จะดึงข้อมูลไคลเอ็นต์และเซิร์ฟเวอร์ gRPC ตัวอย่างของเราที่ส่งออกเมตริกโดยใช้ Prometheus
ดาวน์โหลด Prometheus เวอร์ชันล่าสุด สำหรับแพลตฟอร์มของคุณ จากนั้นแตกไฟล์และดำเนินการ
tar xvfz prometheus-*.tar.gz
cd prometheus-*
สร้างไฟล์การกำหนดค่า Prometheus ด้วยเนื้อหาต่อไปนี้
cat > grpc_otel_java_prometheus.yml <<EOF
scrape_configs:
- job_name: "prometheus"
scrape_interval: 5s
static_configs:
- targets: ["localhost:9090"]
- job_name: "grpc-otel-java"
scrape_interval: 5s
static_configs:
- targets: ["localhost:9464", "localhost:9465"]
EOF
เริ่ม Prometheus ด้วยการกำหนดค่าใหม่โดยใช้คำสั่งต่อไปนี้
./prometheus --config.file=grpc_otel_java_prometheus.yml
การดำเนินการนี้จะกำหนดค่าให้ดึงข้อมูลเมตริกจากกระบวนการ Codelab ของไคลเอ็นต์และเซิร์ฟเวอร์ทุกๆ 5 วินาที
ไปที่ http://localhost:9090/graph เพื่อดูเมตริก ตัวอย่างเช่น คิวรีต่อไปนี้
histogram_quantile(0.5, rate(grpc_client_attempt_duration_seconds_bucket[1m]))
จะแสดงกราฟที่มีค่าความหน่วงแฝงของความพยายามเฉลี่ยโดยใช้ 1 นาทีเป็นหน้าต่างเวลาสำหรับการคำนวณควอนไทล์
อัตราการค้นหา
increase(grpc_client_attempt_duration_seconds_bucket[1m])
6. (ไม่บังคับ) แบบฝึกหัดสำหรับผู้ใช้
ในแดชบอร์ด Prometheus คุณจะเห็นว่า QPS ต่ำ ลองดูว่าคุณจะระบุโค้ดที่น่าสงสัยในตัวอย่างที่จำกัด QPS ได้หรือไม่
สำหรับผู้ที่สนใจ โค้ดไคลเอ็นต์จะจำกัดตัวเองให้มี RPC ที่รอดำเนินการเพียงรายการเดียวในแต่ละครั้ง คุณสามารถแก้ไขโค้ดนี้เพื่อให้ไคลเอ็นต์ส่ง RPC เพิ่มเติมโดยไม่ต้องรอให้ RPC ก่อนหน้าเสร็จสมบูรณ์ (เราไม่ได้ระบุวิธีแก้ปัญหาสำหรับกรณีนี้)