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 FPGA-SRIO初始化失败
  • ¥15 MapReduce实现倒排索引失败
  • ¥15 luckysheet
  • ¥15 ZABBIX6.0L连接数据库报错,如何解决?(操作系统-centos)
  • ¥15 找一位技术过硬的游戏pj程序员
  • ¥15 matlab生成电测深三层曲线模型代码
  • ¥50 随机森林与房贷信用风险模型
  • ¥50 buildozer打包kivy app失败
  • ¥30 在vs2022里运行python代码
  • ¥15 不同尺寸货物如何寻找合适的包装箱型谱