предисловие
В нашем повседневном процессе развития бизнеса мы будем более или менее использовать параллельные функции. Тогда в процессе использования функции параллелизма вы обязательно столкнетесь со следующей проблемой
Насколько велик пул параллельных потоков?
Обычно программисты, которые немного стары, возможно, слышали это утверждение (где N — количество процессоров)
- Приложения с интенсивным использованием ЦП с размером пула потоков, равным N + 1
- Приложения с интенсивным вводом-выводом, размер пула потоков равен 2N.
Это утверждение верно?
На самом деле это крайне неправильно. Так почему?
Прежде всего, давайте посмотрим на это с противоположной стороны, если предположить, что это утверждение верно, то не имеет значения, сколько сервисов мы развертываем на сервере. Поскольку размер пула потоков может быть связан только с количеством ядер на сервере, это утверждение неверно. Итак, как вы должны установить размер?
Предполагая, что это приложение является гибридом двух, в котором задачи интенсивно используют как ЦП, так и ввод-вывод, как мы можем изменить настройки? Разве не только винчестер кинуть решать?
Так как же нам установить размер пула потоков? Существуют ли какие-либо конкретные практические методы, которые помогут вам приземлиться? Давайте посмотрим глубже.
Закон Литтла
Количество запросов к системе равно произведению скорости поступления запросов и среднего времени, затрачиваемого на каждый отдельный запрос.
Предполагая, что сервер имеет одно ядро, соответствующему бизнесу необходимо гарантировать объем запросов (QPS): 10, а обработка запроса занимает 1 секунду, тогда сервер обрабатывает 10 запросов в каждый момент времени, то есть 10 нужны нитки.
Точно так же мы можем использовать закон Литтла для определения размера пула потоков. Нам просто нужно рассчитать скорость поступления запросов и среднее время обработки запросов. Затем, применяя приведенные выше значения к закону Литтла, можно рассчитать среднее количество запросов в системе. Формула оценки выглядит следующим образом
*Размер пула потоков = ((время ввода-вывода потока + время ЦП потока)/время ЦП потока) Количество ЦП**
конкретная практика
Из формулы мы знаем, что нам нужны 3 конкретных значения
- Время, потребляемое запросом (время ввода-вывода потока + время ЦП потока)
- Время вычисления запроса (процессорное время потока)
- Количество процессоров
Потребление времени запроса
В контейнере веб-сервиса время, затраченное до и после получения запроса, может быть перехвачено фильтром.
public class MoniterFilter implements Filter {
private static final Logger logger = LoggerFactory.getLogger(MoniterFilter.class);
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException,
ServletException {
long start = System.currentTimeMillis();
HttpServletRequest httpRequest = (HttpServletRequest) request;
HttpServletResponse httpResponse = (HttpServletResponse) response;
String uri = httpRequest.getRequestURI();
String params = getQueryString(httpRequest);
try {
chain.doFilter(httpRequest, httpResponse);
} finally {
long cost = System.currentTimeMillis() - start;
logger.info("access url [{}{}], cost time [{}] ms )", uri, params, cost);
}
private String getQueryString(HttpServletRequest req) {
StringBuilder buffer = new StringBuilder("?");
Enumeration<String> emParams = req.getParameterNames();
try {
while (emParams.hasMoreElements()) {
String sParam = emParams.nextElement();
String sValues = req.getParameter(sParam);
buffer.append(sParam).append("=").append(sValues).append("&");
}
return buffer.substring(0, buffer.length() - 1);
} catch (Exception e) {
logger.error("get post arguments error", buffer.toString());
}
return "";
}
}
Время вычислений процессора
Время расчета ЦП = общее время запроса - время ввода-вывода ЦП
Предполагая, что в запросе есть операция запроса к БД, если вы знаете, сколько времени (время ввода-вывода процессора) на запрос к БД, время расчета не выйдет. Давайте посмотрим, как записать время, затрачиваемое на запрос к БД. Запрос БД лаконично и ясно.
Добавьте аспект AOP (динамический прокси-сервер JDK / CGLIB), чтобы получить поток ввода-вывода, отнимающий много времени. Код выглядит следующим образом, пожалуйста, обратитесь к:
public class DaoInterceptor implements MethodInterceptor {
private static final Logger logger = LoggerFactory.getLogger(DaoInterceptor.class);
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
StopWatch watch = new StopWatch();
watch.start();
Object result = null;
Throwable t = null;
try {
result = invocation.proceed();
} catch (Throwable e) {
t = e == null ? null : e.getCause();
throw e;
} finally {
watch.stop();
logger.info("({}ms)", watch.getTotalTimeMillis());
}
return result;
}
}
Количество процессоров
Количество логических ЦП, количество ЦП, на которое следует ссылаться при установке размера пула потоков
cat /proc/cpuinfo| grep "processor"| wc -l
Суммировать
На самом деле непросто правильно настроить размер пула потоков, но с помощью приведенной выше формулы и специального кода мы можем быстро и эффективно рассчитать размер пула потоков.
Но, в конце концов, нам все еще нужно внести тонкие коррективы через стресс-тест, и только после проверки стресс-теста мы можем окончательно гарантировать, что размер конфигурации является точным.
Наконец
Прошу всех обратить внимание на мой официальный аккаунт [Программист в погоне за ветром], в нем будут обновляться статьи, а также размещаться отсортированная информация.