使用mgo与上下文一起使用

huangapple go评论99阅读模式
英文:

Using mgo with context

问题

我一直在使用mgo来处理我的API,但是我在我的MongoDB中看到了很多当前的连接(在测试中只使用了不到5个设备)。通过在我的Mongo服务器上执行db.serverStatus().connections,我得到以下结果:{ "current" : 641, "available" : 838219, "totalCreated" : 1136 }。下面是我在mgo的Github上记录的问题(Issue #429):

我的使用方式是否正确?如果不正确,你能给我一个完整的示例吗?

以下代码并不是完整的可运行代码(因为缺少导入部分或配置来源和模型等缺失的部分),但它确切地展示了我如何使用mgo。

我必须澄清,我正在构建一个被多个移动设备和Web应用程序使用的API。

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() // 解析请求体
    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 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

得分: 1

这是一个我见过的人们如何在Go中构建Web应用程序的示例。这段代码未经测试,仅供参考。它缺少导入语句并且可能存在错误。

编辑 添加了一个中间件示例。

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("无法连接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() // 解析请求体
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("错误:%s", err)}
} else if data == nil {
w.WriteHeader(404)
response = models.Response{Status: 404, Message: "设备未找到"}
} else {
response = models.Response{Status: 200, Message: "找到设备", Data: data}
}
// 如果未设置头部,则将头部设置为200
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) {
// 用户授权逻辑
// 也可以将用户存储在请求上下文中
})
}
}

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
}
英文:

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
}

huangapple
  • 本文由 发表于 2017年5月12日 00:36:20
  • 转载请务必保留本文链接:https://go.coder-hub.com/43921168.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定