Подтвердить что ты не робот

Golang: значения сохранения/кэширования, которые должны обслуживаться в следующих http-запросах

Я пишу несколько веб-сервисов Go (также реализуя веб-сервер в Go с http.ListenAndServe). У меня есть карта структур, которые я хотел бы сохранить в памяти (с приблизительным размером данных 100 Кб), которые будут использоваться различными HTTP-запросами.

Каков наилучший способ достичь этого в Go? По вашему опыту, лучше ли использовать глобальные переменные пакета или системы кэширования (например, memcache/groupcache)?

4b9b3361

Ответ 1

В дополнение к ответам, которые вы уже получили, рассмотрите возможность использования значений метода получателя-карри и http. HandlerFunc.

Если ваши данные - это данные, которые загружаются до начала процесса, вы можете пойти примерно так:

type Common struct {
    Data map[string]*Data
}

func NewCommon() (*Common, error) {
    // load data
    return c, err
}

func (c *Common) Root(w http.ResponseWriter, r *http.Request) {
    // handler
}

func (c *Common) Page(w http.ResponseWriter, r *http.Request) {
    // handler
}

func main() {
    common, err := NewCommon()
    if err != nil { ... }

    http.HandleFunc("/", common.Root)
    http.HandleFunc("/page", common.Page)

    http.ListenAndServe(...)
}

Это работает хорошо, если все данные Common доступны только для чтения. Если данные Common считываются/записываются, тогда вам нужно иметь что-то большее:

type Common struct {
    lock sync.RWMutex
    data map[string]Data // Data should probably not have any reference fields
}

func (c *Common) Get(key string) (*Data, bool) {
    c.lock.RLock()
    defer c.lock.RUnlock()
    d, ok := c.data[key]
    return &d, ok
}

func (c *Common) Set(key string, d *Data) {
    c.lock.Lock()
    defer c.lock.Unlock()
    c.data[key] = *d
}

Остальное в основном то же самое, за исключением того, что вместо прямого доступа к данным через поля получателя вы получите доступ к ним через получатели и сеттеры. На веб-сервере, где читается большая часть данных, вам, вероятно, понадобится RWMutex, так что чтение может выполняться одновременно друг с другом. Другим преимуществом второго подхода является то, что вы инкапсулировали данные, поэтому в будущем вы можете добавлять прозрачные записи и/или читать из memcache или группового кэша или что-то в этом роде, если ваше приложение будет испытывать такую ​​потребность.

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

Ответ 2

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

В качестве примера, в прошлом месяце я поставил быстрый hack live (http://ubuntu-edge.info), который занял немного нагрузки из-за новости распространяются вокруг кампании crowdfunding, а процесс Go очень часто загружается на небольшой машине EC2. Данные были тривиальными и были просто кэшированы в памяти, обновлялись один раз в минуту из базы данных, которая обновлялась внешним процессом.

Я обычно смотрю на memcache (и теперь groupcache), когда это делается сам по себе, будет означать повторение большей их функциональности. Например, когда слишком много данных, которые нужно сохранить в памяти, или при потере сервера, может повлиять на производительность всей системы, так как кэшированные данные все сразу исчезают или из-за того, что на производительность влияют конфликты.

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

Ответ 3

Не тратьте время на преждевременную оптимизацию. Определите API-интерфейс Go, чтобы инкапсулировать данные, а затем вы можете изменить реализацию в любое время. Например, просто нацарапать,

package data

type Key struct {
    // . . .
}

type Data struct {
    // . . .
}

var dataMap map[Key]Data

func init() {
    dataMap = make(map[Key]Data)
}

func GetData(key Key) (*Data, error) {
    data := dataMap[key]
    return &data, nil
}