2017-02-14 21 views
1

Гипотетически говоря, правильно ли подключаться к базе данных для каждого запроса и закрываться, когда запрос завершен?Управление соединениями по запросу в Go

Я использую mongodb с mgo для базы данных.

В моем проекте я хотел бы подключиться к определенной базе данных, получив имя базы данных из заголовка запроса (конечно, это объединено с механизмом аутентификации, например JWT в моем приложении). Поток идет что-то вроде:

  1. аутентификации пользователя:

    POST to http://api.app.com/authenticate 
    // which checks the user in a "global" database, 
    // authenticates them and returns a signed JWT token 
    // The token is stored in bolt.db for the authentication mechanism 
    
  2. Некоторые RESTful операции

    POST to http://api.app.com/v1/blog/posts 
    // JWT middleware for each request to /v1* is set up 
    // `Client-Domain` in header is set to a database's name, e.g 'app-com' 
    // so we open a connection to that database and close when 
    // request finishes 
    

Так что мои вопросы:

  1. Возможно ли это? - Я читал о пулах соединений и их повторном использовании, но я еще не много читал о них.
  2. Есть ли лучший способ достижения желаемой функциональности?
  3. Как обеспечить, чтобы сессия закрывалась только после завершения запроса?

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

Update/Решение Я закончил с использованием Go «встроенной в Context пути копирования сеанса и использовать его в любом месте, что нужно сделать какой-либо CRUD оп

Что-то вроде:

func main() { 
    ... 
    // Configure connection and set in global var 
    model.DBSession, err = mgo.DialWithInfo(mongoDBDialInfo) 
    defer model.DBSession.Close() 
    ... 

    n := negroni.Classic() 
    n.Use(negroni.HandlerFunc(Middleware)) 

    ... 
} 

func Middleware(res http.ResponseWriter, req *http.Request, next http.HandlerFunc) { 

    ... 
    db := NewDataStore(clientDomain) 
    // db.Close() is an alias for ds.session.Close(), code for this function is not included in this post 
    // Im still experimenting with this, I need to make sure the session is only closed after a request has completed, currently it does not always do so 
    defer db.Close() 

    ctx := req.Context() 
    ctx = context.WithValue(ctx, auth.DataStore, db) 
    req = req.WithContext(ctx) 
    ... 
} 

func NewDataStore(db string) *DataStore { 
    store := &DataStore{ 
     db: DBSession.Copy().DB(db), 
     session: DBSession.Copy(), 
    } 
    return store 
} 

И затем используйте его в HandlerFunc, пример /v1/system/users:

func getUsers(res http.ResponseWriter, req *http.Request) { 
    db := req.Context().Value(auth.DataStore).(*model.DataStore) 
    users := make([]SystemUser{}, 0) 
    // db.C() is an alias for ds.db.C(), code for this function is not included in this post 
    db.C("system_users").Find(nil).All(&users) 
} 

40% время отклика на исходный метод, с которым я экспериментировал.

ответ

0

Гипотетически это не очень хорошая практика, потому что:

  1. Логика базы данных разбросаны по нескольким пакетам.
  2. Это трудно проверить
  3. Вы не можете использовать DI (в основном это будет трудно поддерживать код)

Отвечая на вопросы:

  1. Да это возможно, но вы не используйте пул соединений внутри них, отправляйте пакет (посмотрите код here, если вы хотите узнать больше о пуле подключений)
  2. Лучше всего создать глобальную переменную, которая содержит соединение с базой данных и закрыть ru приложение прекращает (и не закрывает соединение по каждому запросу)
  3. Как обеспечить, чтобы сеанс закрывался только после завершения запроса < - вы должны проверить ответ на свой запрос на db и закрыть соединение (но я не рекомендую закрывать соединение после запроса, потому что вам нужно будет снова открыть другой запрос и снова закрыть и т. д.)
+0

В своем 2-м ответе вы сказали '' создать глобальную переменную, которая содержит соединение с базой данных ", но это ограничило бы меня только подключением к одной базе данных, правильно? Могу ли я сделать то же самое, создав глобальную срезную переменную, содержащую несколько соединений, и закрою все из них, когда приложение умирает? – borislemke

+0

Это зависит: главным образом потому, что с помощью набора номера вы можете подключиться к нескольким хостам в момент времени, а затем вы можете изменить базу данных Mongo с помощью функции [DB] (https://godoc.org/labix.org/v2/mgo#Session. DB) для изменения базы данных. Тебе решать. Но, imho, более чистая отдельная связь для каждого db. Делая это, вы также можете разделить каждую логику db. – Tinwor

+0

Что вы подразумеваете под логикой db? Каждая база данных имеет одинаковые коллекции. Представьте, что вы реплицируете магазин или аналогичную услугу для хостинга/редактирования веб-сайта SaaS, каждый раз, когда новый пользователь регистрирует и создает веб-сайт, мы запускаем новый контейнер 'docker' для своего« выделенного »веб-сайта, у которого есть собственный сервер' mongodb'. Мы отслеживаем, кто владеет каждым контейнером 'docker' и как получить доступ к ним с нашего« основного »сервера' Go'. Таким образом, разница действительно только в том, кто может получить доступ к этой базе данных из 'main'' Go' сервера. – borislemke