
В большинстве web-приложений встречаются медленные операции (SQL запросы, обращение к внешним ресурсам, дисковые операции, операции генерации страниц и т.д.), которые могут значительно затормозить систему особенно при большой интенсивности запросов. Для оптимизации таких операций и применяется кэширование данных. Оно позволяет выполнять меньше таких операций, а большинству пользователей показывать заранее подготовленные данные.
Наиболее популярная технология кэширования для Web приложений - это MemCache. Но сегодня мы поговорим не о нем. А о разработанной в Google кэширующей библиотеке GroupCache, которая была написана на Go и используется в Google для замены MemCache в таких сервисах, как dl.google.com, Google Fiber, Google Monitoring и др.
К характерным особенностям GroupCache стоит отнести:
- Написан на Go. Работы по портированию для других языков ведутся, но на данный момент они не завершены.
- Из коробки поддерживается шардирование по ключу между пирами
- В отличие от MemCache значительно снижена вероятность потери данных, благодаря механизму обработки данных, который реплицирует данных на другие пиры. А также в случае отсутствие данных в кэше способен прочитать данные из первоисточника.
- Для запуска не требуется отдельный сервер, который требует поддержки. GroupCache - это встраиваемая библиотека
- Отсутствует версионирование значений, т.е. если в кэш попал ключ K со значением V, то все время жизни K в кэше у него будет значение равное V. Также отсутствуют операции явного удаления значений из кэша, операция задания времени жизни. Последнюю проблему легко обойти приписывая к ключам метки периода.
- Поддерживается зеркалирование самых горячих элементов кэша между всеми процессами
Рассмотрим на примере, как можно использовать GroupCache. Пусть у нас есть некоторый web-сервер, который отдает небольшие файлы с диска. В простейшем случае функция получения такого файла будет иметь вид:
import "io/ioutil"
func GetFile(filename string) ([]byte, error) {
data, err := ioutil.ReadFile(filename)
if err != nil {
return nil, err
}
return data, nil
}
Если предположить, что файлов много и к некоторым из них достаточно часто происходит обращение, то можно применить кэширование для оптимизации повторяющихся запросов. Для подключения GroupCache нужно импортировать библиотеку для работы с ним
go get "github.com/golang/groupcache"
После чего импортировать в программу
import "github.com/golang/groupcache"
Все данные в GroupCache хранятся по группам, которые имеют фиксированый размер. Для примера, давайте создадим группу для гранения файлов с именем files и размером 100 MB:
fCache := groupcache.NewGroup("files", 10*1024*1024, groupcache.GetterFunc(getSource))
Здесь getSource функция получения данных из первоисточника (в нашем случае файловой системы) и передачи его в GroupCache.
func getSource(ctx groupcache.Context, key string, dst groupcache.Sink) error {
data, err := ioutil.ReadFile(key)
if err != nil {
return err
}
dst.SetBytes(data)
return nil
}
Функция принимает на вход ctx - контекст вызывающего модуля, который может быть nil; key - ключ кэша, который равен в нашем случае имени файла и объект dst, с помощью которого данные передаются в кэш. В случае неудачной загрузки данных функция возвращает error.
После этого наша функция получения файла через кэш перепишется следующим образом:
func GetFile( filename string ) ( []byte, error ) {
var data []byte
if fCache.Get(nil, filename, groupcache.AllocatingByteSliceSink(&data)) != nil {
return nil, errors.New("no data")
}
return data, nil
}
Все достаточно просто. Но это только случай когда у нас один инстанс. В случае нескольких инстансов нужно дополнительно определить пиры и идентифицировать себя.
p := []string{"http://127.0.0.1:8080","http://127.0.0.1:8081"}
pool := groupcache.NewHTTPPool("http://127.0.0.1:8080")
pool.Set(p...)
Первый элемент в массиве p - это текущий пир. Также надо понимать, что в этом случае доступ к нашей функции должен осуществляться через обработчик HTTP-вызова.
#Go #golang #groupcache #IT #memcache #кэширование #программирование