Лучшая практика для поддержания сеанса MGO

В настоящее время я использую mongodb с mgo lib для веб-приложения, но я не уверен, что способ, которым я его использую, хорош.

package db

import (
    "gopkg.in/mgo.v2"
)

const (
    MongoServerAddr = "192.168.0.104"
    RedisServerAddr = "192.168.0.104"
)

var (
    MongoSession, err = mgo.Dial(MongoServerAddr)

    MDB  = MongoSession.DB("message")
    MCol = MDB.C("new")
    MSav = MDB.C("save")

    UDB  = MongoSession.DB("account")
    UCol = UDB.C("user")
)

Я запускаю сеанс БД и создаю переменные, которые принимают значение коллекции и документа, поэтому, когда мне нужно запросить коллекцию, я использую переменную для ее создания.

Как это :

func UserExist(username string) bool {
    user := Users{}
    err := db.UCol.Find(bson.M{"username": username}).One(&user)
    if err != nil {
        return false
    } else {
        return true
    }
}

Итак, есть ли лучшая практика или эта хороша? Спасибо


person JonathanChaput    schedule 26.10.2014    source источник
comment
Лучше использовать функцию для настройки сеанса базы данных, чем объявления переменных. Одна из причин использования функции заключается в том, что вы можете обработать возврат ошибки Dial. Для UserExist я бы использовал количество документов в результирующем наборе чтобы определить, существует ли документ. Нет необходимости получать фактический документ.   -  person    schedule 26.10.2014
comment
спасибо за подсказку для функции UserExist! Но с функцией для инициализации соединения сеанса, могу ли я сделать это с помощью func init() в пакете db и назначить глобальную переменную для db и коллекции с сеансом возврата? Я просто не уверен, как поддерживать сеанс с открытой базой данных, не создавая mgo.Dial() каждый раз, когда мне это нужно, а также уже инициализировать мою базу данных и коллекцию...   -  person JonathanChaput    schedule 26.10.2014


Ответы (3)


Я предлагаю не использовать такой глобальный сеанс. Вместо этого вы можете создать тип, отвечающий за все взаимодействие с базой данных. Например:

type DataStore struct {
    session *mgo.Session
}

func (ds *DataStore) ucol() *mgo.Collection { ... }

func (ds *DataStore) UserExist(user string) bool { ... }

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

func (s *WebSite) dataStore() *DataStore {
    return &DataStore{s.session.Copy()}
}    

func (s *WebSite) HandleRequest(...) {
    ds := s.dataStore()
    defer ds.Close()
    ...
}

Драйвер mgo в этом случае ведет себя хорошо, так как сеансы кэшируются внутри и повторно используются/обслуживаются. Каждый сеанс также будет поддерживаться независимым сокетом во время использования и может иметь независимые настройки, а также будет иметь независимую обработку ошибок. Это проблемы, с которыми вам в конечном итоге придется иметь дело, если вы используете один глобальный сеанс.

person Gustavo Niemeyer    schedule 26.10.2014
comment
Есть много причин для такой настройки, как вы указали. Вызов session.Copy() особенно важен, и люди, использующие драйвер Mongo в Go, не обращают на него достаточного внимания, учитывая, что он позволяет драйверу в полной мере использовать преимущества параллелизма. Но тогда - вы все об этом знаете .. :P +1 - person Simon Whitehead; 27.10.2014
comment
Итак, вы запускаете первоначальный глобальный сеанс, а затем копируете его при каждом запросе? - person collinglass; 05.03.2015
comment
Мастер-сессия, да. Он не обязательно должен быть глобальным в смысле глобальной переменной. - person Gustavo Niemeyer; 05.03.2015
comment
Спасибо. Есть ли проблемы с согласованностью БД при использовании Copy() вместо Clone()? - person collinglass; 07.03.2015
comment
Быстрый вопрос. Какой тип веб-сайта вы используете? И знаете ли вы какие-либо учебные пособия, демонстрирующие такой подход? - person Julian; 26.01.2016
comment
Позвольте мне сказать прямо: если я обрабатываю 5000 запросов в секунду, я открываю сеансы 5000 дБ в секунду? Это правильно? - person codepushr; 21.04.2016
comment
@GustavoNiemeyer Вы не объясняете, где должен быть инициализирован первый сеанс и каково определение WebSite - person kskyriacou; 08.11.2016
comment
Это работает с несколькими файлами? Скажем, у вас есть main.go, где вы установили соединение. У вас есть файл в api/v1/users.go, где вы возвращаете набор пользователей. Как бы вы получили доступ к сеансу из этого файла users.go? И, как указал @kskyriacou, не могли бы вы уточнить все поля, которые у вас есть в вашем коде, пожалуйста - person borislemke; 29.01.2017
comment
@codepushr нет, это не так. mgo использует внутренний пул соединений сокетов с mongodb. на каждый session.Copy() он возьмет один оттуда. - person Inanc Gumus; 24.07.2017
comment
Этот ответ не является минимальным рабочим примером, как указывали другие. В следующем посте один mgo.Session инициализируется в main() и копируется в рамках одновременно выполняемого метода: mongodb.com/blog/post/ Параллелизм в этом примере просто зацикливается на методе и не является асинхронным. Следовательно, это не очень полезно для упомянутого сценария @codepushr, который требует эффективности очереди и асинхронности. - person Adam Erickson; 19.05.2018

Хотя это и не отвечает на ваш вопрос напрямую, в отношении проверки сеанса mgo вы должны использовать отсрочку/восстановление, поскольку вызовы mgo (даже mgo.session.Ping) вызывают панику. Насколько я могу судить, нет другого способа проверить состояние сеанса mgo (mgo godocs). Вы можете использовать предложение Густаво Нимейера и добавить метод к вашему типу DataStore.

func (d *DataStore) EnsureConnected() {
    defer func() {
        if r := recover(); r != nil {
            //Your reconnect logic here.
        }
    }()

    //Ping panics if session is closed. (see mgo.Session.Panic())  
    d.Ping()
}
person Zamicol    schedule 02.11.2016
comment
Вы можете восстановить в своих обработчиках с промежуточным программным обеспечением восстановления без этого. - person Inanc Gumus; 24.07.2017
comment
@inanc Как промежуточное ПО делает это? DataStore.Ping? - person Zamicol; 25.07.2017
comment
Когда он запаниковал, посмотрите на сообщение об ошибке, а затем снова подключитесь. - person Inanc Gumus; 25.07.2017
comment
У вас есть пример промежуточного программного обеспечения для восстановления? - person Zamicol; 25.07.2017
comment
Я написал свой собственный для MongoDb, но есть общий пример здесь. - person Inanc Gumus; 25.07.2017

В версии go 1.7 наиболее идиоматичным способом обработки сеанса mongo на веб-сервере является использование нового пакета стандартной библиотеки context для написания промежуточного программного обеспечения, которое может прикреплять defer session.Close() всякий раз, когда вызывается контекст запроса Done(). Так что вам не нужно помнить, чтобы закрыть

AttachDeviceCollection = func(next http.Handler) http.Handler {
        return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
            db, err := infra.Cloner()
            if err != nil {
                http.Error(w, err.Error(), http.StatusInternalServerError)
                return
            }
            collection, err := NewDeviceCollection(db)

            if err != nil {
                db.Session.Close()
                http.Error(w, err.Error(), http.StatusInternalServerError)
                return
            }
            ctx := context.WithValue(r.Context(), DeviceRepoKey, collection)
            go func() {
                select {
                case <-ctx.Done():
                    collection.Session.Close()
                }
            }()

            next.ServeHTTP(w, r.WithContext(ctx))
        })
    }
person CESCO    schedule 28.11.2016
comment
Контекст не предназначен для использования в долгоживущих вещах, таких как сеансы монго. Это вонючий антипаттерн. - person Inanc Gumus; 24.07.2017
comment
@InancGumus Какой метод лучше всего подходит для обработки сеанса монго? Я вижу довольно много примеров использования контекста и сеансов монго. - person UnNatural; 15.02.2018
comment
Вы можете просто скопировать сеанс из введенного члена структуры, например, по строке. - person Inanc Gumus; 16.02.2018
comment
@InancGumus, но разве контекст здесь не обеспечивает сеанс для недолговечного обработчика? - person w00t; 27.07.2019