Вообще говоря, моя инфраструктура здесь выигрывает от уровней CDN, OSS и шлюза.Мне не нужно нести сюда слишком много трафика.Пока я не пишу много ДТП, в бэкенде нет узкого места в производительности .
Но на этот раз последнюю функцию оттенков серого нужно совместить с бизнесом.До этого наши оттенки серого основывались на DNS как стандарте для региональных оттенков серого, но теперь трафик должен быть направлен напрямую на серверную часть, чтобы судить.Если нет путь к оттенкам серого, если он закэширован спереди - так что никак, мы можем только пойти прямо к мосту и нести его.
Кратко опишем логику нашего кода: запрос 1 -> логика обработки -> запрос 2 -> запрос 3 -> логика обработки -> оценка оттенков серого -> возврат.
Первоначально сохранил самый оригинальный код без установки какого-либо кеша, который удовлетворял потребности в прошлом, потому что кеш был добавлен в заголовок обратным прокси уровня шлюза, включая s-max-age и max-age.
Затем откройте давление:wrk -c20 -t20 -d1m
:
20 threads and 20 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 195.58ms 73.94ms 592.81ms 68.39%
Req/Sec 5.59 2.83 20.00 63.45%
6155 requests in 1.00m, 3.94MB read
Requests/sec: 102.40
Transfer/sec: 67.19KB
Сначала мы думали, что это из-за того, что количество соединений было слишком маленьким.После увеличения QPS не увеличился, а уменьшился.Пообщавшись с большими парнями, которые являются шлюзами, мы узнали следующие два момента:
- QPS - это не то, сколько вы нажимаете, а еще зависит от вычислительной мощности сервера
- ЦП и память используются только более чем на десять процентов, что указывает на то, что узкого места здесь нет, скорее всего, это ввод-вывод.
Часть ввода-вывода нашего оператора — это только операция базы данных сетевого ввода-вывода, поэтому мы добавляем слой кэша LRU к операции базы данных, которая примерно выглядит так:
let result
if (cache(query) is not empty) {
result = cache(query)
} else {
result = await db(query)
}
cache.set(result)
Конечно, это всего лишь кусок псевдокода.
Потом снова нажали:
2 threads and 400 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 337.68ms 485.34ms 2.00s 84.04%
Req/Sec 79.44 31.82 323.00 81.98%
9237 requests in 1.00m, 5.92MB read
Socket errors: connect 0, read 0, write 0, timeout 4952
Requests/sec: 153.71
Transfer/sec: 100.92KB
Улучшение успеваемости очень ограничено (на самом деле вышеприведенный абзац проблематичен, читатели должны сначала подумать об этом, а потом говорить об этом). просмотрите код вместе со мной, и мы снова начали сомневаться в размере пула соединений.
Мы увеличили максимальное количество соединений в пуле соединений в десять раз, а затем нажали, улучшение все еще не очевидно.
Затем мы провели локальную отладку, и после того, как мы начали ведение журнала, мы обнаружили, что оператор SQL все еще делает повторные запросы, даже несмотря на то, что кеш был. В это время мы вспомнили об этом, потому что мы не объединяли запросы (а объединять запросы непросто , потому что строки запросов разные), параллельные запросы Если входящие данные не были обработаны, они не были сохранены в кэше в это время, и другие запросы будут продолжать запрашивать до тех пор, пока не будет завершена первая волна запросов, что серьезно влияет QPS.
Таким образом, мы кэшируем обещание запроса, а не результат запроса. (На самом деле, я уже делал подобный проект раньше, но какое-то время не вспоминал об этом)
let resultPromise
if (cache(query) is not empty) {
resultPromise = cache(query)
} else {
resultPromise = db(query)
}
cache.set(resultPromise)
result = await resultPromise
Испытание под давлением:
2 threads and 400 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 206.88ms 305.24ms 2.39s 89.53%
Req/Sec 1.46k 580.86 2.65k 71.35%
28138 requests in 10.10s, 18.00MB read
Requests/sec: 2785.71
Transfer/sec: 1.78MB
И использование процессора и памяти в мониторинге также ниже, чем раньше (но, конечно, нет явной тяги QPS), система может обрабатывать 1500% запросов одновременно.
Конечно, чтобы избежать использования кеша навсегда, мы устанавливаем тайм-аут кеша здесь на пять минут и выполняем только тогда, когда кеша нет.cache.set
(Поскольку set будет обновлять счетчик времени кеша), то есть он запрашивается только один раз каждые пять минут, что считается допустимым для наших пользователей (бизнес-партнеров).