برنامه نویسی

معیارها می توانند شما را فریب دهند: اندازه گیری زمان اجرا در محیط های ادغام شده با اتصال

اندازه‌گیری زمان اجرای درخواست‌ها به سرویس‌های خارجی برای نظارت بر عملکرد و بهینه‌سازی حیاتی است. با این حال، هنگامی که اتصالات به این سرویس های خارجی ادغام می شوند، ممکن است به طور ناخواسته بیشتر از زمان درخواست را اندازه گیری کنید. به طور خاص، اگر درخواست ها بیش از حد طولانی شود و اتصالات موجود تمام شود، منطق سفارشی شما ممکن است شامل زمان انتظار برای دریافت اتصال از استخر شود. این می تواند به معیارهای گمراه کننده منجر شود و باعث شود که عملکرد سیستم خود را اشتباه تفسیر کنید. بیایید بررسی کنیم که چگونه این اتفاق می‌افتد و چگونه می‌توانید فریب معیارهای خود را نخورید.

دام: از جمله زمان انتظار در متریک

وقتی همه اتصالات در استخر در حال استفاده هستند، درخواست‌های اضافی باید منتظر بمانند تا اتصال در دسترس قرار گیرد. این زمان انتظار اگر جدا از زمان درخواست واقعی اندازه گیری نشود، می تواند معیارهای شما را تغییر دهد.

سناریو: تمام شدن اتصالات

  1. حالت اولیه: مخزن اتصال شما دارای تعداد ثابتی اتصال است که همه آنها در حال استفاده هستند.
  2. درخواست جدید: یک درخواست جدید وارد می شود اما باید منتظر بمانید تا اتصال در دسترس باشد.
  3. زمان انتظار: درخواست منتظر می ماند (احتمالاً برای مدت زمان قابل توجهی) تا زمانی که اتصال آزاد شود.
  4. زمان درخواست: هنگامی که یک اتصال به دست آمد، درخواست واقعی انجام می شود.

اگر منطق سفارشی شما کل زمان را از زمانی که درخواست تا دریافت پاسخ اندازه گیری می کند، هم زمان انتظار و هم زمان درخواست را در نظر می گیرید.

مثال عملی: بازتولید مشکل در Spring Boot با Apache HttpClient 5

برای نشان دادن اینکه چگونه می‌توانید توسط معیارهای خود در یک محیط کانکشن فریب بخورید، بیایید یک مثال عملی با استفاده از Spring Boot و Apache HttpClient 5 را مرور کنیم. ما یک برنامه Spring Boot ساده راه‌اندازی می‌کنیم که درخواست‌های HTTP را به یک سرویس خارجی ارسال می‌کند. ، زمان اجرای این درخواست ها را اندازه گیری کنید و نشان دهید که چگونه خستگی استخر اتصال می تواند به معیارهای گمراه کننده منجر شود.

برای شبیه سازی تاخیر در سرویس خارجی، از تصویر httpbin Docker استفاده می کنیم. Httpbin یک سرویس درخواست و پاسخ HTTP با استفاده آسان ارائه می دهد که می توانیم از آن برای ایجاد تاخیر مصنوعی در درخواست های خود استفاده کنیم.

@SpringBootApplication
@RestController
public class Server {

    public static void main(String... args) {
        SpringApplication.run(Server.class, args);
    }

    class TimeClientHttpRequestInterceptor implements ClientHttpRequestInterceptor {

        @Override
        public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution)
                throws IOException {
            var t0 = System.currentTimeMillis();
            try {
                return execution.execute(request, body);
            } finally {
                System.out.println("Request took: " + (System.currentTimeMillis() - t0) + "ms");
            }
        }
    }

    @Bean
    public RestClient restClient() {
        var connectionManager = new PoolingHttpClientConnectionManager();
        connectionManager.setMaxTotal(2); // Max number of connections in the pool
        connectionManager.setDefaultMaxPerRoute(2); // Max number of connections per route

        return RestClient.builder()//
                .requestFactory(new HttpComponentsClientHttpRequestFactory(
                        HttpClients.custom().setConnectionManager(connectionManager).build()))
                .baseUrl("http://localhost:9091")//
                .requestInterceptor(new TimeClientHttpRequestInterceptor()).build();
    }

    @GetMapping("https://dev.to/")
    String hello() {
        return restClient().get().uri("/delay/2").retrieve().body(String.class);
    }
}
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

در کد بالا یک رهگیر درخواست (ClientHttpRequestInterceptor) ایجاد کردیم تا زمان اجرای درخواست‌ها به سرویس خارجی تحت حمایت httpbin را اندازه‌گیری کنیم.

ما همچنین به صراحت استخر را روی یک اندازه بسیار کوچک از 2 اتصال تنظیم کردیم تا بازتولید مشکل آسان شود.

اکنون فقط باید httpbin را راه اندازی کنیم، برنامه بوت فنری خود را اجرا کنیم و با استفاده از آن یک آزمایش ساده انجام دهیم ab

$ docker run -p 9091:80 kennethreitz/httpbin
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

ab -n 10 -c 4 http://localhost:8080/
...
Percentage of the requests served within a certain time (ms)
  50%   4049
  66%   4054
  75%   4055
  80%   4055
  90%   4057
  95%   4057
  98%   4057
  99%   4057
 100%   4057 (longest request)
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

Request took: 2021ms
Request took: 2016ms
Request took: 2022ms
Request took: 4040ms
Request took: 4047ms
Request took: 4030ms
Request took: 4037ms
Request took: 4043ms
Request took: 4050ms
Request took: 4034ms
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

اگر به اعداد نگاه کنیم، می‌بینیم که حتی اگر یک تاخیر مصنوعی 2 ثانیه برای سرور خارجی تعیین کرده‌ایم، در واقع برای اکثر درخواست‌ها تاخیر 4 ثانیه‌ای دریافت می‌کنیم. علاوه بر این، ما متوجه شده‌ایم که فقط اولین درخواست‌ها تاخیر پیکربندی‌شده ۲ ثانیه را رعایت می‌کنند، در حالی که درخواست‌های بعدی منجر به تاخیر ۴ ثانیه‌ای می‌شوند.

زمان پروفایل

نمایه سازی هنگام مواجهه با رفتار کد عجیب ضروری است زیرا گلوگاه های عملکرد را شناسایی می کند، مسائل پنهانی مانند نشت حافظه را آشکار می کند و نشان می دهد که برنامه شما چگونه از منابع سیستم استفاده می کند.

این بار برنامه در حال اجرا را با استفاده از JFR در حین اجرای برنامه نمایه می کنیم ab تست بار

$ jcmd  JFR.start name=app-profile  duration=60s filename=app-profile-$(date +%FT%H-%M-%S).jfr
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

$ ab -n 50 -c 4 http://localhost:8080/
...
Percentage of the requests served within a certain time (ms)
  50%   4043
  66%   4051
  75%   4057
  80%   4060
  90%   4066
  95%   4068
  98%   4077
  99%   4077
 100%   4077 (longest request)
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

اگر فایل JFR را باز کنیم و به فلمگراف نگاه کنیم، می بینیم که بیشتر زمان اجرا توسط کلاینت HTTP ما صرف می شود. زمان اجرای مشتری بین انتظار برای پاسخگویی سرویس خارجی ما و انتظار برای دریافت اتصال از استخر تقسیم می شود.

شعله گراف

این توضیح می‌دهد که چرا زمان‌های پاسخی که می‌بینیم دوبرابر تاخیر ثابت مورد انتظار ۲ ثانیه است که برای سرور خارجی خود تنظیم کرده‌ایم. ما یک استخر از 2 اتصال را پیکربندی کردیم. با این حال، در آزمایش خود، ما در حال انجام 4 درخواست همزمان هستیم. بنابراین، تنها 2 درخواست اول در زمان مورد انتظار 2 ثانیه ارائه می شود. درخواست‌های بعدی باید منتظر بمانند تا استخر اتصال را آزاد کند، بنابراین زمان پاسخ مشاهده‌شده افزایش می‌یابد.

اگر دوباره به فلمگراف نگاه کنیم، می‌توانیم علت اندازه‌گیری زمان را نیز دریابیم ClientHttpRequestInterceptor زمان پاسخگویی سرور خارجی را نشان نمی‌دهد، بلکه زمان لازم برای دریافت اتصال از استخر به اضافه زمان لازم برای انجام درخواست واقعی به سرور خارجی را نشان می‌دهد. رهگیر ما در واقع یک stack trace را بسته بندی می کند که در نهایت با یک مدیر استخر تماس می گیرد تا اتصال برقرار کند: PoolingHttpClientConnectionManager

نظارت بر زمان پاسخ هر کلاینت HTTP بهتر است با استفاده از معیارهای داخلی آن انجام شود، زیرا این معیارها به طور خاص برای گرفتن اطلاعات زمان بندی دقیق طراحی شده اند. آنها تمام جنبه های چرخه عمر درخواست HTTP، از جمله کسب اتصال، انتقال داده، و رسیدگی به پاسخ را در نظر می گیرند. این تضمین می کند که اندازه گیری ها دقیق و سازگار با عملکرد واقعی مشتری هستند.

نوشته های مشابه

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *

دکمه بازگشت به بالا