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

اندازهگیری زمان اجرای درخواستها به سرویسهای خارجی برای نظارت بر عملکرد و بهینهسازی حیاتی است. با این حال، هنگامی که اتصالات به این سرویس های خارجی ادغام می شوند، ممکن است به طور ناخواسته بیشتر از زمان درخواست را اندازه گیری کنید. به طور خاص، اگر درخواست ها بیش از حد طولانی شود و اتصالات موجود تمام شود، منطق سفارشی شما ممکن است شامل زمان انتظار برای دریافت اتصال از استخر شود. این می تواند به معیارهای گمراه کننده منجر شود و باعث شود که عملکرد سیستم خود را اشتباه تفسیر کنید. بیایید بررسی کنیم که چگونه این اتفاق میافتد و چگونه میتوانید فریب معیارهای خود را نخورید.
دام: از جمله زمان انتظار در متریک
وقتی همه اتصالات در استخر در حال استفاده هستند، درخواستهای اضافی باید منتظر بمانند تا اتصال در دسترس قرار گیرد. این زمان انتظار اگر جدا از زمان درخواست واقعی اندازه گیری نشود، می تواند معیارهای شما را تغییر دهد.
سناریو: تمام شدن اتصالات
- حالت اولیه: مخزن اتصال شما دارای تعداد ثابتی اتصال است که همه آنها در حال استفاده هستند.
- درخواست جدید: یک درخواست جدید وارد می شود اما باید منتظر بمانید تا اتصال در دسترس باشد.
- زمان انتظار: درخواست منتظر می ماند (احتمالاً برای مدت زمان قابل توجهی) تا زمانی که اتصال آزاد شود.
- زمان درخواست: هنگامی که یک اتصال به دست آمد، درخواست واقعی انجام می شود.
اگر منطق سفارشی شما کل زمان را از زمانی که درخواست تا دریافت پاسخ اندازه گیری می کند، هم زمان انتظار و هم زمان درخواست را در نظر می گیرید.
مثال عملی: بازتولید مشکل در 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، از جمله کسب اتصال، انتقال داده، و رسیدگی به پاسخ را در نظر می گیرند. این تضمین می کند که اندازه گیری ها دقیق و سازگار با عملکرد واقعی مشتری هستند.