Оптимизация нашего интерфейса с помощью Go

Go сервер

Заголовок немного великоват, но, к счастью, в этой статье для оптимизации HTTP-сервисов в основном используется Go, так что ее можно считать второстепенной задачей~

задний план

Быстрое увеличение данных о функциях приводит к высокой задержке интерфейса для получения всех функций в городе.Ниже показано время отклика интерфейса, отображаемое на мониторе.Самое медленное время отклика интерфейса может достигать более 5 с.

cost-time

Решение для оптимизации кэша

Идеи оптимизации кода:

1. Используйте кеш.

1.1 Зачем использовать память вместо Redis?

Анализируя бизнес-требования, текущие данные, которые будут храниться, — это ObjectId.ObjectId — это строка длиной около 14. Мы предполагаем, что ObjectId — это строка длиной в среднем 16, так что объем памяти, занимаемый каждым ObjectId, равен 2. Байт, текущему бизнесу необходимо хранить около 300 000 ObjectId, поэтому память, которую текущему бизнесу необходимо хранить для хранения ObjectId, составляет 0,5 МБ, с которой можно работать в памяти. По сравнению с использованием Redis, он не требует сетевых издержек и более эффективен.

1.2 Инициализация кеша: при запуске службы локальный кеш инициализируется пустым.

1.3 Концепция кешированных версий.

Кэшированная версия предназначена для обновления версии данных в БД после обновления автономной задачи создания объектов.

Следующие три схемы основаны на данных ObjectId хранилища памяти, и стратегии отличаются при обновлении памяти.

Вариант первый

2.1 Обновление кеша

Используйте метод активного обновления кеша, создайте запланированное задание, проверяйте версию данных БД каждую 1 минуту и ​​обновляйте данные в кеше, если они обновляются.

2.2 Недостатки

Если поток обновления кеша запускается отдельно, код будет непросто поддерживать, и будут случаи, когда поток запланированной задачи зависает, что нелегко найти. Кроме того, необходимо заранее настроить соответствующие параметры в коде или внести его в центр конфигурации, а стоимость обслуживания высока.

Вариант 2

3.1 Обновление кеша

Применяется стратегия обновления кэша с пассивным запуском, которая запускается вызовом интерфейса. После поступления запроса проверить соответствие версии данных в текущем кеше версии данных в БД, если версия обновилась, перечитать все данные города, соответствующие текущему запросу, в кэш и вернуть обновленные данные вызывающей стороне.

3.2 Недостатки

Поскольку он запускается пассивно, а кеш обновляется синхронно, легко вызвать случайные сбои, если вызов интерфейса сталкивается с обновлением версии, а данные необходимо обновить в памяти.

3.3 Диаграмма последовательности выполнения услуги

方案二时序图

Вариант 3 (окончательно принятый вариант)

4.1, обновление кеша

Принята стратегия пассивного обновления кеша, которая запускается вызывающей стороной интерфейса. Если в текущем кеше есть данные, он напрямую вернет данные в кеше, а затем проверит, соответствует ли версия данных в текущем кеше версии данных в БД.Если версия обновлена, повторно прочитать все характеристики города, соответствующие текущему запросу в кеш, иначе завершить логику обновления кеша.

4.2 Диаграмма последовательности выполнения услуги

方案三时序图

Схема оптимизации параллелизма

Используйте Goroutines для оптимизации нашей последовательной логики

Самая большая особенность языка Go заключается в том, что он поддерживает параллелизм (Горутина) на уровне языка.Горутина — это самая основная исполнительная единица в Go. На самом деле в каждой программе Go есть по крайней мере одна горутина: главная горутина. Он создается автоматически при запуске программы.

Чтобы лучше понять Goroutine, давайте поговорим о понятиях потоков и сопрограмм:

Поток. Иногда его называют облегченным процессом (LWP). Это наименьшая единица потока выполнения программы. Стандартный поток состоит из идентификатора потока, указателя текущей инструкции (PC), набора регистров и стека. Кроме того, поток является сущностью процесса, и он представляет собой базовую единицу, независимо планируемую и диспетчеризируемую системой.Сам поток не владеет системными ресурсами, а лишь несколькими ресурсами, необходимыми для выполнения.Потоки разделяют все ресурсы, принадлежащие процессу.

Потоки имеют свои собственные независимые стеки и общие кучи, общие кучи, но не общие стеки, а переключение потоков обычно планируется операционной системой.

Сопрограмма (сопрограмма): также известная как микропоток и подпрограмма (или функция), сопрограмма (сопрограмма) также является программным компонентом. По сравнению с подпрограммами сопрограммы являются более общими и гибкими, но не так широко используются на практике, как подпрограммы.

Подобно потокам, куча является общей, но стек не является общим, а переключение сопрограмм обычно явно контролируется программистом в коде. Он позволяет избежать дополнительных затрат на переключение контекста, учитывает преимущества многопоточности и упрощает сложные программы с высокой степенью параллелизма.

карта в golang небезопасна для потоков

Очевидно, что мы можем использовать механизм блокировки для решения проблемы одновременного чтения и записи Map. Мы изменили приведенную выше структуру карты на следующую:

// M
type M struct {
    Map    map[string]string
    lock sync.RWMutex // 加锁
}

// Set ...
func (m *M) Set(key, value string) {
    m.lock.Lock()
    defer m.lock.Unlock()
    m.Map[key] = value
}

// Get ...
func (m *M) Get(key string) string {
    return m.Map[key]
}

В приведенном выше коде мы вводим работу механизма блокировки, тем самым обеспечивая безопасность карты в нескольких горутинах.

Оптимизируем нашу логику, используя шаблон стратегии

В основном это связано с тем, что в коде слишком много операторов if/else, поэтому шаблон стратегии используется для оптимизации структуры нашего кода. Вот первая статья, найденная в Интернетестатья, а потом у меня будет время написать об этом отдельную статью. Оптимизированный код на 50% меньше, чем предыдущий код, который понятнее и проще в обслуживании. Ниже приведен эффект после того, как оптимизированный код переходит в режим онлайн, а время запроса составляет менее 100 мс:

监控接口耗时

резюме

Вышеизложенное обычно представляет собой общее решение, когда наш интерфейс занимает много времени.Конечно, конкретные проблемы должны быть проанализированы подробно.Поэтому, когда интерфейс работает медленно, мы должны проанализировать конкретные причины медленного ответа интерфейса. правильное лекарство!

Подписывайтесь на нас

关注我们