doukuang8166 2017-05-11 16:36
浏览 6
已采纳

在上下文中使用mgo

I have been using mgo for my API but I'm seeing many current connections in my MongoDB (while using less than 5 devices for testing). By executing db.serverStatus().connections in my Mongo server I get: { "current" : 641, "available" : 838219, "totalCreated" : 1136 }. Below I transcript my issue in mgo's Github (Issue #429):

Is my way of using mgo in a web server the correct way? If not, can you give me a full example?

This code is not functional, take it as almost pseudo code (because of the missing parts like the imports or where the configs come from and models), but it is exactly how I am using mgo.

I must clarify that I'm building an API which is used by several mobile devices and webapps.

main.go

func main() {
    mongoDBDialInfo := &mgo.DialInfo{
        Addrs:    []string{config.DatabaseURL},
        Timeout:  60 * time.Second,
        Database: config.DatabaseName,
        Username: config.DatabaseUsername,
        Password: config.DatabasePassword,
    }

    db, err := mgo.DialWithInfo(mongoDBDialInfo)

    if err != nil {
        log.Fatal("Cannot Dial Mongo: ", err)
    }

    defer db.Close() 
    db.SetMode(mgo.Monotonic, true)

    phoneIndex := mgo.Index{
        Key:        []string{"pp"},
        Unique:     true,
        DropDups:   true,
        Background: true,
        Sparse:     true,
    }

    err = db.DB(config.DatabaseName).C("users").EnsureIndex(phoneIndex)
    if err != nil {
        panic(err)
    }

    router := mux.NewRouter()
    router.HandleFunc("/login", publicWithDB(login, db)).Methods("POST")

    if err := http.ListenAndServe(":5000", router); err != nil {
        log.Fatal(err)
    }
}

func publicWithDB(fn http.HandlerFunc, db *mgo.Session) http.HandlerFunc {
    return func(w http.ResponseWriter, r *http.Request) {
        dbsession := db.Copy()
        defer dbsession.Close()
        fn(w, r.WithContext(context.WithValue(r.Context(), contextKeyDatabase, dbsession)))
    }
}

func login(w http.ResponseWriter, r *http.Request) {
    r.ParseForm() // Parses the request body
    device := r.Form.Get("device")

    var deviceid bson.ObjectId
    if bson.IsObjectIdHex(device) {
        deviceid = bson.ObjectIdHex(device)
    }

    db := r.Context().Value(contextKeyDatabase).(*mgo.Session)

    var device models.Device
    err := db.DB(config.DatabaseName).C("devices").FindId(deviceid).One(&device)

    w.WriteHeader(200)
    w.Write([]byte(utils.ResponseToString(models.Response{Status: 200, Message: "asdasd", Data: device})))

}

I'm posting this because I couldn't find a complete implementation.

  • 写回答

1条回答 默认 最新

  • duanaiguang1960 2017-05-14 07:26
    关注

    Here's an example of how I have seen myself and others structure web apps in Go. This code is untested and is purely for example. It's missing imports and potentially has errors.

    EDIT Added a middleware example.

    main.go

    package main
    
    func main() {
        mongoDBDialInfo := &mgo.DialInfo{
            Addrs:    []string{config.DatabaseURL},
            Timeout:  60 * time.Second,
            Database: config.DatabaseName,
            Username: config.DatabaseUsername,
            Password: config.DatabasePassword,
        }
    
        db, err := mgo.DialWithInfo(mongoDBDialInfo)
    
        if err != nil {
            log.Fatal("Cannot Dial Mongo: ", err)
        }
    
        defer db.Close() 
        db.SetMode(mgo.Monotonic, true)
    
        phoneIndex := mgo.Index{
            Key:        []string{"pp"},
            Unique:     true,
            DropDups:   true,
            Background: true,
            Sparse:     true,
        }
    
        err = db.DB(config.DatabaseName).C("users").EnsureIndex(phoneIndex)
        if err != nil {
            panic(err)
        }
    
        mgoAdapter := mongo.NewAdapter(db, config.DatabaseName)
        deviceStore := mongo.NewDeviceStore(mgoAdapter)
        userStore := mongo.NewUserStore(mgoAdapter)
    
        loginController := controllers.NewLoginController(deviceStore)
    
        router := mux.NewRouter()
        router.HandleFunc("/login", middleware.AuthorizeUser(userStore)(http.HandlerFunc(loginController.Login)).Methods("POST")
    
        if err := http.ListenAndServe(":5000", router); err != nil {
            log.Fatal(err)
        }
    }
    

    controllers/login.go

    package controllers
    
    type LoginController struct { 
        store DeviceStore
    }
    
    func NewLoginController(store stores.DeviceStore) *LoginController {
        return &LoginController{
            store: store,
        }
    }
    
    func (c *LoginController) Login(w http.ResponseWriter, r *http.Request) {
        r.ParseForm() // Parses the request body
        device := r.Form.Get("device")
        data, err := c.store.FindByDevice(device)
    
        var respose models.Response
        if err != nil {
            w.WriteHeader(500)
            response = models.Response{Status: 500, Message: fmt.Sprintf("error: %s", err)}
        } else if data == nil {
            w.WriteHeader(404)
            response = models.Response{Status: 404, Message: "device not found"}
        } else {
            response = models.Response{Status: 200, Message: "device found", Data: data}
        }
    
        // Write sets header to 200 if it hasn't been set already
        w.Write([]byte(utils.ResponseToString(response)))
    }
    

    stores/stores.go

    package stores
    
    type DeviceStore interface {
        FindByDevice(device string) (*models.Device, error)
    }
    
    type UserStore interface {
        FindByToken(token string) (*models.User, error)
    }
    

    middleware/auth.go

    package middleware
    
    func AuthorizeUser(store stores.UserStore) func(h *http.Handler) http.Handler {
        return func(h *http.Handler) http.Handler {
            return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
                // Logic for authorizing user
                // Could also store user in the request context
            })
        }
    }
    

    mongo/mongo.go

    package mongo
    
    type Adapter struct {
        session      *mgo.Session
        databaseName string
    }
    
    func NewAdapter(session *mgo.Session, dbName string) *Adapter {
        return &Adapter{session: session, databaseName: dbName}
    }
    
    type deviceStore struct {
        *Adapter
    }
    
    func NewDeviceStore(adapter *Adapter) stores.DeviceStore {
        return &deviceStore{adapter}
    }
    
    const devices = "devices"
    
    func (s *deviceStore) FindByDevice(d string) (*models.Device, err) {
        sess := s.session.copy()
        defer sess.close()
    
        var deviceID bson.ObjectId
        if bson.IsObjectIdHex(d) {
            deviceID = bson.ObjectIdHex(d)
        }
    
        var device models.Device
        err := db.DB(s.databaseName).C(devices).FindId(deviceID).One(&device)
        if err == mgo.ErrNotFound {
            return nil, nil
        }
        return &device, err
    }
    
    type userStore struct {
        *Adapter
    }
    
    const users = "users"
    
    func NewUserStore(adapter *Adapter) stores.UserStore {
        return &userStore{adapter}
    }
    
    func (s *userStore) GetUserByToken(token string) (*models.User, error) {
        sess := s.session.copy()
        defer sess.close()
    
        var user models.User
        err := db.DB(s.databaseName).C(users).Find(bson.M{"token": token}).One(&user)
        if err == mgo.ErrNotFound {
            return nil, nil
        }
    
        return &user, err
    }
    
    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥15 有了解d3和topogram.js库的吗?有偿请教
  • ¥100 任意维数的K均值聚类
  • ¥15 stamps做sbas-insar,时序沉降图怎么画
  • ¥15 unity第一人称射击小游戏,有demo,在原脚本的基础上进行修改以达到要求
  • ¥15 买了个传感器,根据商家发的代码和步骤使用但是代码报错了不会改,有没有人可以看看
  • ¥15 关于#Java#的问题,如何解决?
  • ¥15 加热介质是液体,换热器壳侧导热系数和总的导热系数怎么算
  • ¥100 嵌入式系统基于PIC16F882和热敏电阻的数字温度计
  • ¥15 cmd cl 0x000007b
  • ¥20 BAPI_PR_CHANGE how to add account assignment information for service line