Что такое высокий параллелизм?
High concurrency is one of the performance indicators of Internet distributed system architecture. It usually refers to the number of requests that the system can handle at the same time per unit time. In short, it's QPS (Queries per second).
Так о чем же мы говорим, когда говорим о высоком параллелизме?
Что такое высокий параллелизм?
Здесь мы сначала делаем вывод: Высокий параллелизм Базовая производительность-это количество запросов, которые система может обрабатывать одновременно в единицу времени. Высокий уровень параллелизма Ядро – это ресурсы процессора. Эффективное нажатие 。
Например, если мы разрабатываем программу под названием Исчерпание MD5 , каждый запрос содержит зашифрованную строку MD5. Наконец, система исчерпывает все результаты и возвращает исходную строку. В настоящее время наш сценарий приложения или бизнес приложений относится к интенсивному использованию процессора Вместо интенсивного ввода-вывода . В настоящее время процессор выполняет эффективные вычисления, даже при полной загрузке процессора, когда мы говорим о высоком параллелизме, бессмысленно. (Конечно, мы можем улучшить возможности параллелизма, добавив машины, то есть добавив процессоры. Это нормальная обезьяна, знающая бессмысленную схему. Бессмысленно говорить об арифмометрах. Никакой высокий параллелизм не может быть решен с помощью вычислительных машин. Если это так, это означает, что вы добавляете недостаточно машин! (2)
Для большинства интернет-приложений процессор не является и не должен быть узким местом системы. Большую часть времени система находится в состоянии, когда процессор ожидает завершения операции ввода-вывода (жесткий диск/память/сеть) для чтения/записи.
В этот момент кто-то может сказать, что, когда я наблюдаю за системным мониторингом, память и сеть в норме, но загрузка процессора полная. Почему?
This is a good question. I will give some practical examples later. I will emphasize the four words "effective squeezing" mentioned above again. These four words will revolve around the whole content of this article.
Способ управления переменной
Все взаимосвязано. Когда мы говорим о высоком параллелизме, каждое звено системы должно быть согласовано. Давайте сначала рассмотрим классический процесс HTTP-запроса C/S.
Как показано серийным номером на рисунке: 1 Мы проанализируем DNS-сервер и запросим, чтобы он прибыл в кластер балансировки нагрузки. 2 Серверы балансировки нагрузки будут распределять запросы на уровень обслуживания в соответствии с правилами конфигурации. Уровень обслуживания также является основным уровнем нашего бизнеса. Также могут быть некоторые звонки из КНР, MQ и так далее. 3 проходит через уровень кэша 4 Последние постоянные данные 5 Возвращает данные клиенту
Для достижения высокого параллелизма нам необходима высокая доступность и высокая производительность балансировки нагрузки, уровня обслуживания, уровня кэша и уровня сохраняемости. Даже на шаге 5 мы можем оптимизировать его, сжимая статические файлы, HTTP 2, передавая статические файлы, CDN. Мы можем написать несколько книг по каждому слою здесь, чтобы обсудить оптимизацию.
В этой статье в основном обсуждается уровень обслуживания, то есть часть красной катушки. Влияние описания базы данных и кэширования больше не рассматривается. Знания средней школы говорят нам, что это называется Метод переменной управления 。
Повторное обсуждение вопроса о параллелизме
- История эволюции модели сетевого программирования
Параллелизм всегда был ключевой и сложной проблемой в программировании на стороне сервера. Чтобы оптимизировать параллелизм системы, от начального процесса разветвления до пула процессов/пула потоков, а затем до epoll, управляемого событиями (Nginx, node.js античеловеческий обратный вызов), а затем к сопрограмме. Из вышесказанного очевидно, что весь процесс эволюции-это процесс повышения эффективной производительности процессора. Что? Не очевидно?
- Давайте поговорим о переключении контекста.
Прежде чем мы поговорим о переключении контекста, давайте проясним понятия двух существительных. Параллель: Два события завершаются одновременно. Одновременно: Два события происходят попеременно в один и тот же период времени. С макро-точки зрения происходят оба события. 。
Потоки являются наименьшей единицей планирования операционной системы, а процессы-наименьшей единицей распределения ресурсов. Поскольку процессор является последовательным, для одноядерного процессора должен быть только один поток, занимающий ресурсы процессора одновременно. В результате в Linux как многозадачной (процессной) системе переключение процессов/потоков происходит часто.
Перед выполнением каждой задачи процессор должен знать, где загружать и где запускать, и эта информация хранится в Регистре процессора И операционная система Счетчик программ Внутри, эти две вещи называются Контекст процессора . Процессы управляются и планируются ядром, и переключение процессов может происходить только в состоянии ядра. Поэтому виртуальная память, стек, глобальные переменные и другие ресурсы пользовательского пространства, а также состояние пространства ядра, такое как стек ядра и регистры, называются Контекст процесса . Как упоминалось ранее, потоки являются наименьшей единицей планирования операционной системы. В то же время потоки совместно используют такие ресурсы, как виртуальная память и глобальные переменные родительского процесса, поэтому ресурс родительского процесса плюс его собственные личные данные в Интернете называется Контекст потоков 。
Для переключения потоков в контексте потоки одного и того же процесса потребляют меньше ресурсов, чем переключение между несколькими процессами, из-за совместного использования ресурсов.
Теперь легче объяснить, что произойдет переключение между процессами и потоками. Контекст процессора Переключение и Контекст процесса/потока Переключение. И это все. Переключение контекста Все они потребляют дополнительные ресурсы процессора.
- Дальнейшее обсуждение переключения контекста протокола
Требуется ли для совместной работы переключение контекста? Нужно, но Этого не случится. Переключение контекста процессора и Контекст процесса/потока Потому что все эти переключатели находятся в одном потоке, то есть в пользовательском режиме. Вы даже можете просто понять это как , Контекст сопроцесса Переключение между ними заключается в перемещении указателя в вашей программе, ресурсы процессора по-прежнему принадлежат текущему потоку. Нужно глубокое понимание, вы можете глубже взглянуть на модель Gos GMP 。 Конечным результатом является сотрудничество. Дальнейшее повышение эффективности использования процессора 。
Вернемся к вопросу в начале
At this point, someone might say, when I watch the system monitoring, memory and network are normal, but CPU utilization is full. Why?
Обратите внимание, что когда в этой статье говорится об использовании ЦП, в ней необходимо добавить эффективный Как атрибут, коэффициент загрузки процессора работает в полную силу, и во многих случаях он на самом деле делает много неэффективных вычислений. Возьмем в качестве примера “лучший язык в мире”, типичный режим PHP-FPM CGI, каждый HTTP-запрос: Из фреймворка считываются сотни PHP-файлов. Все соединения MYSQL/REIDS/MQ восстановлены/освобождены. Они будут динамически интерпретировать, компилировать и выполнять PHP-файлы. В различных процессах php -fpm они будут переключаться и переключаться напрямую и непрерывно.
Режим работы Php CGI , который в основном определяет его высокий параллелизм Катастрофическую производительность 。
Найти проблемы часто бывает сложнее, чем их решить. Когда мы понимаем Когда мы говорим о высоком параллелизме, о чем мы говорим? Позже мы обнаружим, что высокий параллелизм и высокая производительность ограничены не языками программирования, а вашими идеями.
Найдите проблему и решите ее! Какого эффекта мы можем достичь, если сможем эффективно снизить производительность процессора?
Давайте рассмотрим различия в производительности между HTTP-сервисами PHP + swool и высокопроизводительными асинхронными HTTP-сервисами netty на Java.
Подготовьтесь к сравнению производительности
- Что такое опухоль?
Swoole is an event-based high-performance asynchronous-coroutine parallel network communication engine written for PHP in C and C++.
- Что такое Нетти
Netty is a Java open source framework provided by JBOSS. Netty provides an asynchronous, event-driven framework and tools for rapid development of high-performance, high-reliability network servers and client programs.
- Какое максимальное количество HTTP-подключений может достичь одна машина?
Напомним соответствующие знания компьютерных сетей, протокол HTTP является протоколом прикладного уровня, на транспортном уровне каждое TCP-соединение будет установлено до трех рукопожатий. Каждое TCP-соединение выполняется локальным ip , Локальным портом , Удаленным IP , Удаленным портом Четырьмя идентификаторами атрибутов. Заголовок протокола TCP выглядит следующим образом (изображение из Википедии):
Локальные порты состоят из 16 бит, поэтому максимальное количество локальных портов равно 2 ^. Удаленный порт состоит из 16 бит, поэтому максимальное количество удаленных портов равно 2 ^. В то же время в базовой модели сетевого программирования linux для каждого TCP-соединения операционная система поддерживает файл дескриптора файла (fd), соответствующий ему. Ограничение числа FD может быть просмотрено и изменено командой ulimit-n. Перед тестированием мы можем выполнить команду: ulimit-n 65536, чтобы изменить ограничение на 65535.
Таким образом, без учета ограничения аппаратных ресурсов Максимальное количество локальных HTTP-соединений составляет: максимальное количество локальных портов 65535 * количество локальных IP-адресов. Максимальное количество удаленных HTTP-соединений: максимальное количество удаленных портов составляет 65535 * количество удаленных (клиентских) IP-адресов + не ограничено. PS: На самом деле в операционной системе будет занято несколько зарезервированных портов, поэтому количество локальных подключений на самом деле не соответствует теоретическому значению.
Сравнение производительности
- Ресурсы для тестирования
Каждый контейнер docker, 1 г памяти + 2 ядра процессора, как показано на рисунке:
Docker-compose устроен следующим образом:
# java8 version: "2.2" services: java8: container_name: "java8" hostname: "java8" image: "java:8" volumes: - /home/cg/MyApp:/MyApp ports: - "5555:8080" environment: - TZ=Asia/Shanghai working_dir: /MyApp cpus: 2 cpuset: 0,1 mem_limit: 1024m memswap_limit: 1024m mem_reservation: 1024m tty: true # php7-sw version: "2.2" services: php7-sw: container_name: "php7-sw" hostname: "php7-sw" image: "mileschou/swoole:7.1" volumes: - /home/cg/MyApp:/MyApp ports: - "5551:8080" environment: - TZ=Asia/Shanghai working_dir: /MyApp cpus: 2 cpuset: 0,1 mem_limit: 1024m memswap_limit: 1024m mem_reservation: 1024m tty: true
- php-код
set([
'worker_num' => 2
]);
$http->on("request", function ($request, Response $response) {
//go(function () use ($response) {
// Swoole\Coroutine::sleep(0.01);
$response->end('Hello World');
//});
});
$http->on("start", function (Server $server) {
go(function () use ($server) {
echo "server listen on 0.0.0.0:8080 \n";
});
});
$http->start();
- Код ключа Java
Исходный код взят из https://github.com/netty/netty
public static void main(String[] args) throws Exception {
// Configure SSL.
final SslContext sslCtx;
if (SSL) {
SelfSignedCertificate ssc = new SelfSignedCertificate();
sslCtx = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()).build();
} else {
sslCtx = null;
}
// Configure the server.
EventLoopGroup bossGroup = new NioEventLoopGroup(2);
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
ServerBootstrap b = new ServerBootstrap();
b.option(ChannelOption.SO_BACKLOG, 1024);
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.handler(new LoggingHandler(LogLevel.INFO))
.childHandler(new HttpHelloWorldServerInitializer(sslCtx));
Channel ch = b.bind(PORT).sync().channel();
System.err.println("Open your web browser and navigate to " +
(SSL? "https" : "http") + "://127.0.0.1:" + PORT + '/');
ch.closeFuture().sync();
} finally {
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
Поскольку я предоставил ресурсы процессора только двум ядрам, обе службы открыли только один рабочий процесс. Порт 5551 представляет службу PHP. Порт 5555 представляет службы Java.
- Сравнение результатов инструментов для измерения давления: Стенд Apache (ab)
Команда Ab: запуск докера — RM jordi/ab-k-c 1000-n 1000000 http://10.234.3.32:5555/При сравнительном анализе 1 миллиона Http-запросов одновременно,
Результаты измерения давления Java + netty:
Манометрические результаты PHP + swool:
| 84042.11 | Java + нетти | 600+ | (11,25) |
| 87222.98 | php + свул | 30+ | (9,25) |
Ps: Наилучший результат измерения кубического давления выбран на рисунке выше.
В целом, разница в производительности невелика. Сервис PHP + swool даже немного лучше, чем у Java + netty. Особенно с точки зрения использования памяти, Java использует 600 МБ, а PHP использует только 30 МБ. Что это значит? Без операции блокировки ввода-вывода переключение совместного процесса не произойдет. Это показывает только то, что в многопоточном режиме + epoll вы можете эффективно снизить производительность процессора и даже создавать сервисы с высоким параллелизмом и высокой производительностью с помощью PHP.
Контраст производительности – Время стать свидетелем чудес
На самом деле, приведенный выше код не показывает отличную производительность протокола, потому что весь запрос не имеет операции блокировки, но часто наше приложение будет сопровождаться различными операциями блокировки, такими как чтение документов, подключение к БД/запрос. Теперь давайте посмотрим, как происходит измерение давления при блокировке. В коде Java и PHP я добавляю Спящий режим (0,01)/секунды Код для имитации блокировки системных вызовов в течение 0,01 секунды. Код больше не повторяется.
Манометрические результаты Java + netty с блокировкой ввода-вывода:
Для выполнения всей манометрии требуется около 10 минут…
Манометрические результаты PHP + swool с блокировкой ввода-вывода:
| 1562.69 | Java + нетти | 100+ | (52,160) |
| 9745.20 | php + свул | 30+ | (9,25) |
Как вы можете видеть из результатов, служба PHP + swool на основе протокола в шесть раз превосходит QPS службы Java + netty.
Конечно, эти два тестовых кода являются исходным кодом официальной демо-версии, должно быть много настроек, которые можно оптимизировать, после оптимизации результаты, безусловно, будут намного лучше.
Подумайте еще раз, почему официальное количество потоков/процессов по умолчанию не установлено немного больше? Чем больше процессов/потоков, тем лучше. Как мы обсуждали ранее, при переключении процессов/потоков возникнут дополнительные накладные расходы на ресурсы процессора, особенно при переключении между пользовательским режимом и режимом ядра.
Для этих измерений я не ориентируюсь на Java. Я имею в виду, что если вы понимаете, в чем суть высокого параллелизма, и находите эту цель, независимо от того, какой язык программирования вы используете, до тех пор, пока вы эффективно оптимизируете загрузку ЦП (пул подключений, демон, многопоточность, совместная работа, опрос выбора, управляемый событиями epoll), вы также можете воспользоваться этим. Создайте систему с высоким уровнем параллелизма и высокой производительностью.
Итак, теперь вы видите, когда мы говорим о высокой производительности, о чем мы говорим?
Мысли всегда важнее результатов.