เราใช้สถาปัตยกรรมไมโครเซอร์วิสในโครงการของเรา เมื่อเกิดปัญหาคอขวดด้านประสิทธิภาพ จะต้องใช้เวลามากในการตรวจสอบและแยกวิเคราะห์บันทึก เมื่อบันทึกการกำหนดเวลาของการดำเนินการแต่ละรายการลงในไฟล์บันทึก มักจะยากที่จะเข้าใจว่าอะไรนำไปสู่การเรียกใช้การดำเนินการเหล่านี้ เพื่อติดตามลำดับของการดำเนินการหรือการเปลี่ยนแปลงเวลาของการดำเนินการหนึ่งที่เกี่ยวข้องกับอีกบริการหนึ่ง
เพื่อลดการใช้แรงงานคน เราตัดสินใจใช้เครื่องมือติดตามตัวใดตัวหนึ่ง เกี่ยวกับวิธีการและเหตุผลที่คุณสามารถใช้การติดตามและวิธีที่เราใช้ ซึ่งจะมีการกล่าวถึงในบทความนี้
ปัญหาใดที่สามารถแก้ไขได้ด้วยการติดตาม
- ค้นหาคอขวดของประสิทธิภาพทั้งภายในบริการเดียวและในแผนผังการดำเนินการทั้งหมดระหว่างบริการที่เข้าร่วมทั้งหมด ตัวอย่างเช่น:
- การเรียกสั้นๆ ติดต่อกันหลายๆ ครั้งระหว่างบริการต่างๆ เช่น เพื่อ geocoding หรือไปยังฐานข้อมูล
- การรอ I/O ที่ยาวนาน เช่น การโอนย้ายเครือข่ายหรือการอ่านดิสก์
- การแยกวิเคราะห์ข้อมูลแบบยาว
- การทำงานที่ยาวนานต้องใช้ซีพียู
- ส่วนของโค้ดที่ไม่จำเป็นเพื่อให้ได้ผลลัพธ์สุดท้าย และสามารถลบออกหรือเลื่อนออกไปได้
- เข้าใจอย่างชัดเจนว่าลำดับใดที่เรียกว่าอะไรและจะเกิดอะไรขึ้นเมื่อดำเนินการ
จะเห็นได้ว่า ตัวอย่างเช่น คำขอมาที่บริการ WS -> บริการ WS เพิ่มข้อมูลผ่านบริการ R -> จากนั้นส่งคำขอไปยังบริการ V -> บริการ V โหลดข้อมูลจำนวนมากจากบริการ R -> ไปที่บริการ P -> บริการ P ไปที่บริการ R อีกครั้ง -> บริการ V เพิกเฉยต่อผลลัพธ์และไปที่บริการ J -> จากนั้นจึงส่งคืนการตอบกลับไปยังบริการ WS เท่านั้น ในขณะที่ดำเนินการคำนวณอย่างอื่นอยู่เบื้องหลัง
หากไม่มีการติดตามหรือเอกสารโดยละเอียดสำหรับกระบวนการทั้งหมด ก็เป็นเรื่องยากมากที่จะเข้าใจว่าเกิดอะไรขึ้นเมื่อดูโค้ดเป็นครั้งแรก และโค้ดจะกระจัดกระจายไปตามบริการต่างๆ และซ่อนอยู่หลังถังขยะและอินเทอร์เฟซจำนวนมาก - การรวบรวมข้อมูลเกี่ยวกับแผนผังการดำเนินการสำหรับการวิเคราะห์ที่เลื่อนออกไปในภายหลัง ในแต่ละขั้นตอนของการดำเนินการ คุณสามารถเพิ่มข้อมูลไปยังการติดตามที่พร้อมใช้งานในขั้นตอนนี้ จากนั้นค้นหาว่าข้อมูลอินพุตใดที่นำไปสู่สถานการณ์ที่คล้ายกัน ตัวอย่างเช่น:
- รหัสผู้ใช้
- สิทธิมนุษยชน
- ประเภทของวิธีที่เลือก
- บันทึกหรือดำเนินการผิดพลาด
- เปลี่ยนการติดตามเป็นส่วนย่อยของเมตริกและการวิเคราะห์เพิ่มเติมในรูปแบบของเมตริก
ร่องรอยใดที่สามารถบันทึกได้ ช่วง
ในการติดตามมีแนวคิดของช่วง ซึ่งเป็นอะนาล็อกของบันทึกหนึ่งไปยังคอนโซล สปามี:
- ชื่อ โดยปกติจะเป็นชื่อของเมธอดที่ถูกเรียกใช้งาน
- ชื่อของบริการที่สร้างช่วง
- ID เฉพาะของตัวเอง
- ข้อมูลเมตาบางประเภทในรูปแบบของคีย์/ค่าที่เข้าสู่ระบบ ตัวอย่างเช่น พารามิเตอร์เมธอดหรือเมธอดจบลงด้วยข้อผิดพลาดหรือไม่
- เวลาเริ่มต้นและสิ้นสุดสำหรับช่วงเวลานี้
- รหัสช่วงผู้ปกครอง
แต่ละช่วงจะถูกส่งไปยังตัวรวบรวมช่วงเพื่อเก็บไว้ในฐานข้อมูลเพื่อตรวจสอบในภายหลังทันทีที่ดำเนินการเสร็จสิ้น ในอนาคต คุณสามารถสร้างแผนผังของช่วงทั้งหมดได้โดยเชื่อมต่อด้วยรหัสพาเรนต์ เมื่อทำการวิเคราะห์ คุณจะพบ เช่น ระยะเวลาทั้งหมดในบริการบางอย่างที่ใช้เวลานานกว่าระยะหนึ่ง นอกจากนี้ เมื่อไปที่ช่วงใดช่วงหนึ่ง ให้ดูทั้งต้นไม้ด้านบนและด้านล่างของช่วงนี้
Opentrace, Jagger และวิธีที่เราใช้ในโครงการของเรา
มีมาตรฐานร่วมกัน
เรากำลังใช้
- Jaeger-agent เป็น local agent ที่โดยปกติจะติดตั้งในแต่ละเครื่องและบริการต่างๆ จะถูกล็อกอินบนพอร์ตเริ่มต้นในเครื่อง หากไม่มีตัวแทน การติดตามบริการทั้งหมดบนเครื่องนี้มักจะถูกปิดใช้งาน
- Jaeger-collector - ตัวแทนทั้งหมดส่งการติดตามที่รวบรวมได้และวางไว้ในฐานข้อมูลที่เลือก
- ฐานข้อมูลเป็นคาสซานดราที่พวกเขาต้องการ แต่เราใช้ elasticsearch มีการใช้งานสำหรับฐานข้อมูลอื่นสองสามฐานข้อมูลและการใช้งานในหน่วยความจำที่ไม่ได้บันทึกอะไรลงในดิสก์
- Jaeger-query เป็นบริการที่ไปยังฐานข้อมูลและส่งคืนร่องรอยที่รวบรวมไว้แล้วสำหรับการวิเคราะห์
- Jaeger-ui เป็นเว็บอินเตอร์เฟสสำหรับค้นหาและดูร่องรอย โดยจะไปที่ jaeger-query
คอมโพเนนต์ที่แยกจากกันสามารถเรียกว่าการดำเนินการของ opentrace jaeger สำหรับภาษาเฉพาะ ซึ่งส่งผ่านช่วงไปยัง jaeger-agent
คุณสามารถเชื่อมต่อสำหรับส่วนประกอบสปริงได้
บันทึกการติดตามใน Java
ที่ระดับบนสุด ต้องสร้าง Span แรก ซึ่งสามารถทำได้โดยอัตโนมัติ เช่น โดยสปริงคอนโทรลเลอร์เมื่อได้รับการร้องขอ หรือด้วยตนเองหากไม่มี จากนั้นจะถูกส่งผ่านขอบเขตด้านล่าง หากเมธอดใดๆ ด้านล่างต้องการเพิ่ม Span ระบบจะใช้ ActiveSpan ปัจจุบันจากขอบเขต สร้าง Span ใหม่และบอกว่าพาเรนต์คือ ActiveSpan ที่เป็นผลลัพธ์ และทำให้ Span ใหม่ใช้งานได้ เมื่อเรียกใช้บริการจากภายนอก สแปนที่ใช้งานอยู่ในปัจจุบันจะถูกส่งต่อไปยังบริการเหล่านั้น และบริการเหล่านั้นจะสร้างสแปนใหม่โดยอ้างอิงจากสแปนนี้
งานทั้งหมดต้องผ่านอินสแตนซ์ Tracer คุณสามารถรับได้ผ่านกลไก DI หรือ GlobalTracer.get () เป็นตัวแปรส่วนกลางหากกลไก DI ไม่ทำงาน ตามค่าเริ่มต้น ถ้าตัวติดตามไม่ได้ถูกเตรียมใช้งาน NoopTracer จะกลับมาโดยไม่ทำอะไรเลย
นอกจากนี้ ขอบเขตปัจจุบันได้รับจากตัวติดตามผ่าน ScopeManager ขอบเขตใหม่จะถูกสร้างขึ้นจากขอบเขตปัจจุบันโดยผูกกับช่วงใหม่ จากนั้นขอบเขตที่สร้างขึ้นจะปิด ซึ่งจะปิดช่วงที่สร้างขึ้นและส่งกลับขอบเขตก่อนหน้าไปยังสถานะใช้งาน ขอบเขตเชื่อมโยงกับเธรด ดังนั้นเมื่อเขียนโปรแกรมแบบมัลติเธรด คุณต้องไม่ลืมโอนช่วงที่ใช้งานไปยังเธรดอื่น เพื่อเปิดใช้งานขอบเขตของเธรดอื่นเพิ่มเติมโดยอ้างอิงถึงสแปนนี้
io.opentracing.Tracer tracer = ...; // GlobalTracer.get()
void DoSmth () {
try (Scope scope = tracer.buildSpan("DoSmth").startActive(true)) {
...
}
}
void DoOther () {
Span span = tracer.buildSpan("someWork").start();
try (Scope scope = tracer.scopeManager().activate(span, false)) {
// Do things.
} catch(Exception ex) {
Tags.ERROR.set(span, true);
span.log(Map.of(Fields.EVENT, "error", Fields.ERROR_OBJECT, ex, Fields.MESSAGE, ex.getMessage()));
} finally {
span.finish();
}
}
void DoAsync () {
try (Scope scope = tracer.buildSpan("ServiceHandlerSpan").startActive(false)) {
...
final Span span = scope.span();
doAsyncWork(() -> {
// STEP 2 ABOVE: reactivate the Span in the callback, passing true to
// startActive() if/when the Span must be finished.
try (Scope scope = tracer.scopeManager().activate(span, false)) {
...
}
});
}
}
สำหรับการเขียนโปรแกรมแบบมัลติเธรด ยังมี TracedExecutorService และ wrapper ที่คล้ายกันซึ่งจะส่งต่อช่วงปัจจุบันไปยังเธรดโดยอัตโนมัติเมื่อเปิดใช้งานงานแบบอะซิงโครนัส:
private ExecutorService executor = new TracedExecutorService(
Executors.newFixedThreadPool(10), GlobalTracer.get()
);
สำหรับคำขอ HTTP ภายนอกมี
HttpClient httpClient = new TracingHttpClientBuilder().build();
ปัญหาที่เราประสบ
- ดังนั้น Beans และ DI จะไม่ทำงานเสมอไปหากไม่ได้ใช้ตัวติดตามในบริการหรือส่วนประกอบ
อัตโนมัติ Tracer อาจใช้งานไม่ได้ และคุณจะต้องใช้ GlobalTracer.get() - คำอธิบายประกอบจะไม่ทำงานหากไม่ใช่คอมโพเนนต์หรือบริการ หรือหากเมธอดถูกเรียกจากเมธอดข้างเคียงของคลาสเดียวกัน คุณต้องระมัดระวังในการตรวจสอบว่าอะไรใช้งานได้และใช้การสร้างการติดตามด้วยตนเองหาก @Traced ไม่ทำงาน คุณยังสามารถแนบคอมไพเลอร์เพิ่มเติมสำหรับคำอธิบายประกอบของจาวา ซึ่งควรจะทำงานได้ทุกที่
- ในสปริงและสปริงบูตแบบเก่า การกำหนดค่าอัตโนมัติของ opentraing spring cloud ไม่ทำงานเนื่องจากข้อบกพร่องใน DI ดังนั้นหากคุณต้องการให้ร่องรอยในส่วนประกอบสปริงทำงานโดยอัตโนมัติ คุณสามารถทำได้โดยเปรียบเทียบกับ
github.com/opentracing-contrib/java-spring-jaeger/blob/master/opentracing-spring-jaeger-starter/src/main/java/io/opentracing/contrib/java/spring/jaeger/starter/JaegerAutoConfiguration.java - การลองด้วยทรัพยากรใช้งานไม่ได้ใน Groovy คุณต้องใช้การลองในที่สุด
- แต่ละบริการต้องมี spring.application.name ของตัวเอง ซึ่งการติดตามจะถูกบันทึก ชื่อแยกต่างหากสำหรับการขายและการทดสอบคืออะไร เพื่อไม่ให้รบกวนพวกเขาด้วยกัน
- หากคุณใช้ GlobalTracer และ Tomcat บริการทั้งหมดที่ทำงานใน Tomcat นี้จะมี GlobalTracer เดียว ดังนั้นบริการทั้งหมดจะมีชื่อบริการเหมือนกัน
- เมื่อเพิ่มการติดตามให้กับเมธอด คุณต้องแน่ใจว่าไม่ได้เรียกซ้ำหลายครั้ง จำเป็นต้องเพิ่มหนึ่งการติดตามทั่วไปสำหรับการโทรทั้งหมด ซึ่งรับประกันเวลาทำงานทั้งหมด มิฉะนั้นจะมีการสร้างภาระส่วนเกิน
- ครั้งหนึ่งใน jaeger-ui มีการร้องขอจำนวนมากเกินไปสำหรับการติดตามจำนวนมาก และเนื่องจากพวกเขาไม่รอการตอบกลับ พวกเขาจึงทำอีกครั้ง ผลที่ตามมาคือ jaeger-query เริ่มกินหน่วยความจำจำนวนมากและทำให้ความยืดหยุ่นช้าลง ช่วยโดยการรีสตาร์ท jaeger-query
การสุ่มตัวอย่าง การจัดเก็บ และการดูร่องรอย
มีสามประเภท
- Const ซึ่งส่งและบันทึกร่องรอยทั้งหมด
- ความน่าจะเป็นที่กรองร่องรอยด้วยความน่าจะเป็นที่กำหนด
- Ratelimiting ซึ่งจำกัดจำนวนการติดตามต่อวินาที คุณสามารถกำหนดการตั้งค่าเหล่านี้บนไคลเอ็นต์ บน jaeger-agent หรือบนตัวรวบรวม ตอนนี้เราใช้ const 1 ใน valuator stack เนื่องจากมีคำขอไม่มากนัก แต่ใช้เวลานาน ในอนาคต ถ้าสิ่งนี้จะทำให้ระบบโหลดมากเกินไป คุณสามารถจำกัดได้
หากคุณใช้คาสซานดรา ตามค่าเริ่มต้นจะเก็บร่องรอยไว้เพียงสองวันเท่านั้น เรากำลังใช้
ในการดูร่องรอยที่คุณต้องการ:
- เลือกบริการที่คุณต้องการกรองการติดตาม ตัวอย่างเช่น tomcat7-default สำหรับบริการที่ทำงานใน Tomcat และไม่สามารถมีชื่อของตัวเองได้
- จากนั้นเลือกการดำเนินการ ช่วงเวลา และเวลาการดำเนินการขั้นต่ำ เช่น จาก 10 วินาที เพื่อใช้เวลาการดำเนินการที่ยาวนานเท่านั้น
- ไปที่หนึ่งในร่องรอยและดูว่ามีอะไรช้าลงที่นั่น
นอกจากนี้ หากทราบรหัสคำขอบางรายการ คุณสามารถค้นหาการติดตามด้วยรหัสนี้ผ่านการค้นหาแท็ก หากรหัสนี้ถูกบันทึกไว้ในช่วงการติดตาม
เอกสาร
- เอกสารเปิดการติดตาม
opentracing.io/docs/overview/อะไรคือการติดตาม - เอกสารแจเกอร์
www.jaegertracing.io/docs/1.10 - การเชื่อมต่อ Java ของ Jaeger
github.com/jaegertracing/jaeger-client-java - สปริงเปิดการเชื่อมต่อการติดตาม
github.com/jaegertracing/jaeger-client-java
github.com/opentracing-contrib/java-spring-cloud
บทความ
habr.com/ru/company/carprice/blog/340946 Jaeger Opentracing และ Microservices ในโครงการ PHP และ Golang จริงwww.uber.com/distributed-tracing การพัฒนาการติดตามแบบกระจายที่ Uber Engineeringopentracing.io/guides/java medium.com/jaegertracing/running-jaeger-agent-on-bare-metal-d1fc47d31fab ใช้ Jaeger Agent บนโลหะเปล่า
วีดีโอ
www.youtube.com/watch?v=qg0ENOdP1Lo วิธีที่เราใช้ Jaeger และ Prometheus เพื่อส่งข้อความค้นหาของผู้ใช้ที่รวดเร็วปานสายฟ้า — Bryan Borehamwww.youtube.com/watch?v=WRntQsUajow บทนำ: Jaeger - Yuri Shkuro, Uber & Pavol Loffay, Red Hatwww.youtube.com/watch?v=fsHb0qK37bc Serghei Iakovlev, “เรื่องเล็กๆ ของชัยชนะที่ยิ่งใหญ่: OpenTracing, AWS และ Jaeger”
ที่มา: will.com