web-dev-qa-db-ja.com

mgoセッションを維持するためのベストプラクティス

現在、Webアプリケーションにmgo libでmongodbを使用していますが、使用方法が良いものかどうかはわかりません..

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")
)

Dbセッションを初期化し、コレクションとドキュメントの値を取得する変数を作成するため、コレクションを照会する必要がある場合は、変数を使用して作成します。

そのように:

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
    }
}

だからベストプラクティスがありますか、これは大丈夫です..?ありがとう

26
JonathanChaput

そのようなグローバルセッションを使用しないことをお勧めします。代わりに、すべてのデータベース相互作用を担当するタイプを作成できます。例えば:

type DataStore struct {
    session *mgo.Session
}

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

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

その設計には多くの利点があります。重要なのは、複数のセッションを同時に飛行できるようにすることです。たとえば、httpハンドラーがある場合は、その1つのリクエストに対してのみ、独立したセッションによってバッキングされるローカルセッションを作成できます。

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

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

セッションは内部的にキャッシュされ、再利用/維持されるため、mgoドライバーはその場合に適切に動作します。また、各セッションは使用中に独立したソケットによってサポートされ、独立した設定が構成されている場合があり、独立したエラー処理もあります。これらは、単一のグローバルセッションを使用している場合に最終的に対処する必要がある問題です。

63

質問に直接答えるわけではありませんが、mgoセッションのチェックに関しては、mgoが(mgo.session.Pingでも)パニックを起こすため、延期/回復を使用する必要があります。私が知る限り、mgoセッション状態をチェックする他の方法はありません( mgo godocs )。 Gustavo Niemeyer の提案を使用して、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()
}
2
Zamicol

Go 1.7では、Webサーバーでmongoセッションを処理する最も慣用的な方法は、新しい標準ライブラリパッケージcontextを使用して、リクエストコンテキストが完了したときにdefer session.Close()をアタッチできるミドルウェアを記述することです。 ()が呼び出されます。したがって、閉じるために覚えておく必要はありません

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))
        })
    }
0
CESCO