Theo dõi dịch vụ, OpenTracing và Jaeger

Theo dõi dịch vụ, OpenTracing và Jaeger

Chúng tôi sử dụng kiến ​​trúc microservice trong các dự án của mình. Khi tắc nghẽn hiệu suất xảy ra, rất nhiều thời gian được sử dụng để theo dõi và phân tích nhật ký. Khi ghi thời gian của các hoạt động riêng lẻ vào một tệp nhật ký, thường rất khó hiểu điều gì đã dẫn đến việc gọi các hoạt động này, để theo dõi chuỗi hành động hoặc sự thay đổi thời gian của một hoạt động so với hoạt động khác trong các dịch vụ khác nhau.

Để giảm thiểu lao động thủ công, chúng tôi quyết định sử dụng một trong các công cụ theo dõi. Về cách thức và lý do tại sao bạn có thể sử dụng tính năng theo dõi cũng như cách chúng tôi đã thực hiện việc này sẽ được thảo luận trong bài viết này.

Những vấn đề có thể được giải quyết với truy tìm

  1. Tìm các tắc nghẽn hiệu suất cả trong một dịch vụ và trong toàn bộ cây thực thi giữa tất cả các dịch vụ tham gia. Ví dụ:
    • Nhiều cuộc gọi ngắn liên tiếp giữa các dịch vụ, chẳng hạn như mã hóa địa lý hoặc cơ sở dữ liệu.
    • Thời gian chờ I/O lâu, chẳng hạn như chuyển mạng hoặc đọc đĩa.
    • Phân tích dữ liệu dài.
    • Hoạt động lâu đòi hỏi cpu.
    • Các phần mã không cần thiết để có được kết quả cuối cùng và có thể bị xóa hoặc trì hoãn.
  2. Hiểu rõ những gì được gọi theo trình tự nào và điều gì xảy ra khi thao tác được thực hiện.
    Theo dõi dịch vụ, OpenTracing và Jaeger
    Có thể thấy ví dụ Request đến dịch vụ WS -> dịch vụ WS thêm dữ liệu qua dịch vụ R -> sau đó gửi yêu cầu đến dịch vụ V -> dịch vụ V tải rất nhiều dữ liệu từ dịch vụ R dịch vụ -> đã chuyển sang dịch vụ P -> dịch vụ P lại chuyển sang dịch vụ R -> dịch vụ V bỏ qua kết quả và chuyển sang dịch vụ J -> và chỉ sau đó trả lại phản hồi cho dịch vụ WS, trong khi tiếp tục tính toán thứ khác trong lý lịch.
    Nếu không có dấu vết hoặc tài liệu chi tiết như vậy cho toàn bộ quá trình, rất khó để hiểu điều gì đang xảy ra khi nhìn vào mã lần đầu tiên và mã nằm rải rác trên các dịch vụ khác nhau và ẩn sau một loạt ngăn và giao diện.
  3. Thu thập thông tin về cây thực thi để phân tích hoãn lại tiếp theo. Ở mỗi giai đoạn thực hiện, bạn có thể thêm thông tin vào dấu vết có sẵn ở giai đoạn này và sau đó tìm ra dữ liệu đầu vào nào dẫn đến tình huống tương tự. Ví dụ:
    • Tên người dùng
    • Quyền
    • Loại phương pháp đã chọn
    • Nhật ký hoặc lỗi thực thi
  4. Biến các dấu vết thành một tập hợp con các số liệu và phân tích sâu hơn đã có ở dạng số liệu.

Những gì dấu vết có thể đăng nhập. kéo dài

Trong truy tìm có khái niệm về một khoảng, đây là một dạng tương tự của một nhật ký, đối với bảng điều khiển. Spa có:

  • Tên, thường là tên của phương thức đã được thực thi
  • Tên của dịch vụ trong đó nhịp được tạo
  • Sở hữu ID duy nhất
  • Một số loại thông tin meta ở dạng khóa/giá trị đã được đăng nhập vào đó. Ví dụ: tham số phương thức hoặc kết thúc phương thức có lỗi hay không
  • Thời gian bắt đầu và kết thúc cho khoảng thời gian này
  • ID nhịp gốc

Mỗi nhịp được gửi đến trình thu thập nhịp để được lưu trữ trong cơ sở dữ liệu để xem xét sau ngay khi nó hoàn thành việc thực hiện. Trong tương lai, bạn có thể xây dựng một cây gồm tất cả các nhịp bằng cách kết nối theo id gốc. Khi phân tích, bạn có thể tìm thấy, ví dụ, tất cả các khoảng trong một số dịch vụ đã mất nhiều thời gian hơn. Hơn nữa, bằng cách đi đến một nhịp cụ thể, hãy xem toàn bộ cây bên trên và bên dưới nhịp này.

Theo dõi dịch vụ, OpenTracing và Jaeger

Opentrace, Jagger và cách chúng tôi triển khai nó cho các dự án của mình

Có một tiêu chuẩn chung opentrace, mô tả cách thức và nội dung nên được thu thập mà không bị ràng buộc bằng cách truy tìm một triển khai cụ thể bằng bất kỳ ngôn ngữ nào. Ví dụ: trong Java, tất cả công việc với các dấu vết đều được thực hiện thông qua API Opentrace chung và bên dưới nó, chẳng hạn như Jaeger hoặc triển khai mặc định trống không có gì có thể ẩn được.
Chúng tôi đang sử dụng Jaeger như một triển khai của Opentrace. Nó bao gồm một số thành phần:

Theo dõi dịch vụ, OpenTracing và Jaeger

  • Jaeger-agent là một tác nhân cục bộ thường được cài đặt trên mỗi máy và các dịch vụ được đăng nhập vào nó trên cổng mặc định cục bộ. Nếu không có tác nhân, thì dấu vết của tất cả các dịch vụ trên máy này thường bị vô hiệu hóa
  • Jaeger-collector - tất cả các tác nhân gửi dấu vết đã thu thập đến nó và nó sẽ đưa chúng vào cơ sở dữ liệu đã chọn
  • Cơ sở dữ liệu là cassandra ưa thích của họ, nhưng chúng tôi sử dụng elaticsearch, có các triển khai cho một vài cơ sở dữ liệu khác và triển khai trong bộ nhớ không lưu bất kỳ thứ gì vào đĩa
  • Jaeger-query là một dịch vụ truy cập cơ sở dữ liệu và trả về các dấu vết đã được thu thập để phân tích
  • Jaeger-ui là một giao diện web để tìm kiếm và xem các dấu vết, nó đi đến jaeger-query

Theo dõi dịch vụ, OpenTracing và Jaeger

Một thành phần riêng biệt có thể được gọi là triển khai opentrace jaeger cho các ngôn ngữ cụ thể, qua đó các nhịp được gửi đến jaeger-agent.
Kết nối Jagger trong Java bắt đầu triển khai giao diện io.opentracing.Tracer, sau đó tất cả các dấu vết thông qua giao diện đó sẽ chuyển đến tác nhân thực.

Theo dõi dịch vụ, OpenTracing và Jaeger

Ngoài ra đối với thành phần lò xo, bạn có thể kết nối opentracing-spring-cloud-starter và thực hiện từ Jaeger opentracing-spring-jaeger-cloud-starter sẽ tự động định cấu hình theo dõi mọi thứ đi qua các thành phần này, ví dụ: yêu cầu http tới bộ điều khiển, yêu cầu tới cơ sở dữ liệu thông qua jdbc, v.v.

Dấu vết đăng nhập trong Java

Ở đâu đó ở cấp cao nhất, Span đầu tiên phải được tạo, điều này có thể được thực hiện tự động, chẳng hạn như bởi bộ điều khiển lò xo khi nhận được yêu cầu hoặc theo cách thủ công nếu không có. Sau đó nó được truyền qua Scope bên dưới. Nếu bất kỳ phương pháp nào bên dưới muốn thêm một Span, thì nó sẽ lấy activeSpan hiện tại từ Phạm vi, tạo một Span mới và cho biết cha của nó là activeSpan kết quả và làm cho Span mới hoạt động. Khi gọi các dịch vụ bên ngoài, khoảng hoạt động hiện tại được chuyển cho chúng và các dịch vụ đó tạo các khoảng mới có tham chiếu đến khoảng này.
Tất cả công việc đều thông qua cá thể Tracer, bạn có thể lấy nó thông qua cơ chế DI hoặc GlobalTracer.get() dưới dạng biến toàn cục nếu cơ chế DI không hoạt động. Theo mặc định, nếu trình theo dõi chưa được khởi tạo, NoopTracer sẽ trả về không có tác dụng gì.
Hơn nữa, phạm vi hiện tại được lấy từ trình theo dõi thông qua Trình quản lý phạm vi, một phạm vi mới được tạo từ phạm vi hiện tại với sự ràng buộc của khoảng mới, sau đó Phạm vi đã tạo được đóng lại, thao tác này sẽ đóng khoảng đã tạo và trả lại Phạm vi trước đó cho trạng thái hoạt động. Phạm vi được gắn với một luồng, vì vậy khi lập trình đa luồng, bạn không được quên chuyển khoảng hoạt động sang luồng khác, để tiếp tục kích hoạt Phạm vi của luồng khác có tham chiếu đến khoảng này.

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)) {
                ...
            }
        });
    }
}

Đối với lập trình đa luồng, cũng có TracedExecutorService và các trình bao bọc tương tự tự động chuyển tiếp khoảng hiện tại tới luồng khi các tác vụ không đồng bộ được khởi chạy:

private ExecutorService executor = new TracedExecutorService(
    Executors.newFixedThreadPool(10), GlobalTracer.get()
);

Đối với các yêu cầu http bên ngoài, có Truy TìmHttpClient

HttpClient httpClient = new TracingHttpClientBuilder().build();

Những vấn đề chúng tôi gặp phải

  • Đậu và DI không phải lúc nào cũng hoạt động nếu trình theo dõi không được sử dụng trong một dịch vụ hoặc thành phần, thì có dây tự động Tracer có thể không hoạt động và bạn sẽ phải sử dụng GlobalTracer.get().
  • Chú thích không hoạt động nếu nó không phải là một thành phần hoặc dịch vụ hoặc nếu phương thức được gọi từ một phương thức lân cận của cùng một lớp. Bạn phải cẩn thận kiểm tra những gì hoạt động và sử dụng tính năng tạo dấu vết thủ công nếu @Traced không hoạt động. Bạn cũng có thể đính kèm một trình biên dịch bổ sung cho các chú thích java, sau đó chúng sẽ hoạt động ở mọi nơi.
  • Trong khởi động mùa xuân và khởi động mùa xuân cũ, cấu hình tự động cấu hình đám mây mùa xuân opentraing không hoạt động do lỗi trong DI, sau đó nếu bạn muốn dấu vết trong các thành phần mùa xuân hoạt động tự động, bạn có thể thực hiện bằng cách tương tự với 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
  • Thử với các tài nguyên không hoạt động trong Groovy, bạn phải sử dụng thử cuối cùng.
  • Mỗi dịch vụ phải có spring.application.name riêng, theo đó các dấu vết sẽ được ghi lại. Tên riêng cho việc bán hàng và thử nghiệm là gì, để không can thiệp vào chúng cùng nhau.
  • Nếu bạn sử dụng GlobalTracer và tomcat, thì tất cả các dịch vụ đang chạy trong tomcat này đều có một GlobalTracer, vì vậy tất cả chúng sẽ có cùng tên dịch vụ.
  • Khi thêm dấu vết vào một phương thức, bạn cần chắc chắn rằng nó không được gọi nhiều lần trong một vòng lặp. Cần phải thêm một dấu vết chung cho tất cả các cuộc gọi, đảm bảo tổng thời gian làm việc. Nếu không, tải trọng dư thừa sẽ được tạo ra.
  • Khi ở jaeger-ui, các yêu cầu quá lớn đã được đưa ra đối với một số lượng lớn dấu vết và vì họ không đợi phản hồi nên họ đã thực hiện lại. Kết quả là, truy vấn jaeger bắt đầu ăn nhiều bộ nhớ và làm chậm đàn hồi. Được trợ giúp bằng cách khởi động lại jaeger-query

Lấy mẫu, lưu trữ và xem dấu vết

Có ba loại dấu vết lấy mẫu:

  1. Const gửi và lưu tất cả các dấu vết.
  2. Xác suất lọc dấu vết với một số xác suất nhất định.
  3. Ratelimiting giới hạn số lượng dấu vết mỗi giây. Bạn có thể định cấu hình các cài đặt này trên máy khách, trên jaeger-agent hoặc trên bộ sưu tập. Bây giờ chúng tôi sử dụng const 1 trong ngăn xếp định giá, vì không có nhiều yêu cầu nhưng chúng mất nhiều thời gian. Trong tương lai, nếu điều này gây quá tải cho hệ thống, bạn có thể hạn chế nó.

Nếu bạn dùng cassandra thì mặc định nó chỉ lưu vết trong hai ngày. Chúng tôi đang sử dụng nghiên cứu và dấu vết được lưu trữ mãi mãi và không bị xóa. Một chỉ mục riêng được tạo cho mỗi ngày, ví dụ jaeger-service-2019-03-04. Trong tương lai, bạn cần định cấu hình tự động xóa các dấu vết cũ.

Để xem các dấu vết bạn cần:

  • Chọn dịch vụ mà bạn muốn lọc dấu vết, ví dụ: tomcat7-default cho một dịch vụ đang chạy trong tomcat và không thể có tên riêng.
  • Sau đó chọn thao tác, khoảng thời gian và thời gian thao tác tối thiểu, ví dụ từ 10 giây, để chỉ thực hiện các thao tác dài.
    Theo dõi dịch vụ, OpenTracing và Jaeger
  • Đi đến một trong những dấu vết và xem những gì đang chậm lại ở đó.
    Theo dõi dịch vụ, OpenTracing và Jaeger

Ngoài ra, nếu đã biết một số id yêu cầu, thì bạn có thể tìm dấu vết theo id này thông qua tìm kiếm thẻ, nếu id này được ghi vào khoảng theo dõi.

Tài liệu

bài viết

Video

Nguồn: www.habr.com

Thêm một lời nhận xét