dongwenghe2416 2018-04-09 08:08
浏览 14

通过服务器状态更改HTTP路由

I'm building a REST server in Go and using mongoDB as my database (but this question is actually related to any other external resource).

I want my server to start and respond, even if the database is not up yet (not when the database is down after the server started - this is a different issue, much easier one).

So my dao package includes a connection go-routine that receives a boolean channel, and write 'true' to the channel when it successfully connected to the database. If the connection failed, the go-routine will keep trying every X seconds.

When I use this package with another software I wrote, that is a just a command-line program, I'm using select with timeout:

dbConnected := make(chan bool)
storage.Connect(dbConnected)

timeout := time.After(time.Minute)

select {
case <-dbConnected:
    createReport()

case <-timeout:
    log.Fatalln("Can't connect to the database")
}

I want to use the same packge in a server, but I don't want to fail the whole server. Instead, I want to start the server with handler that returns 503 SERVER BUSY, until the server is connected to the database, and then start serve requests normally. Is there a simple way to implement this logic in go standard library? Using solutions like gorilla is an option, but the server is simple with very few APIs, and gorilla is a bit overkill.

== edited: ==

I know I can use a middleware but I don't know how to do that without sharing data between the main method and the handlers. That why I'm using the channel in the first place.

  • 写回答

1条回答 默认 最新

  • dougan4663 2018-04-09 11:43
    关注

    I have something working, but it does based on common data. However, the data is a single boolean, so I guess it's not so dramatic. I would love to get comments for this solution:

    In the dao package, I have this Connect method, that return a boolean channel. The private connect go routine, writes 'true' and exit when succeed:

    func Connect() chan bool {
        connected := make(chan bool)
        go connect(mongoUrl, connected)
        return connected
    }
    

    I also added the Ping() method to the dao package; it run forever and monitor the database status. It reports the status to a new channel and try to reconnect if needed:

    func Ping() chan bool {
        status := make(chan bool)
        go func() {
            for {
                if err := session.Ping(); err != nil {
                    session.Close()
                    status <- false
                    <- Connect()
                    status <- true
                }
                time.Sleep(time.Second)
            }
        }()
    
        return status
    } 
    

    In the main package, I have this simple type:

    type Connected struct {
        isConnected bool
    }
    
    // this one is called as go-routine 
    func (c *Connected) check(dbConnected chan bool) {
        // first connection, on server boot
        c.isConnected = <- dbConnected
    
        // monitor the database status
        status := dao.Ping()
        for {
            c.isConnected = <- status
        }
    }
    
    // the middleware
    func (c *Connected) checkDbHandleFunc(next http.HandlerFunc) http.HandlerFunc {
        return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
            if !c.isConnected {
                w.Header().Add("Retry-After", "10")
                w.Header().Set("Content-Type", "application/json")
                w.WriteHeader(503)
                respBody := `{"error":"The server is busy; Try again soon"}`
                w.Write([]byte(respBody))
            } else {
                next.ServeHTTP(w, r)
            }
        })
    }
    

    Middleware usage:

    ...
    connected := Connected{
        isConnected: false,
    }
    
    dbConnected := dao.Connect()
    go connected.check(dbConnected)
    
    mux := http.NewServeMux()
    mux.HandleFunc("/", mainPage)
    mux.HandleFunc("/some-db-required-path/", connected.checkDbHandleFunc(someDbRequiredHandler))
    ...
    log.Fatal(http.ListenAndServe(addr, mux))
    ...
    

    Does it make sense?

    评论

报告相同问题?

悬赏问题

  • ¥15 R语言Rstudio突然无法启动
  • ¥15 关于#matlab#的问题:提取2个图像的变量作为另外一个图像像元的移动量,计算新的位置创建新的图像并提取第二个图像的变量到新的图像
  • ¥15 改算法,照着压缩包里边,参考其他代码封装的格式 写到main函数里
  • ¥15 用windows做服务的同志有吗
  • ¥60 求一个简单的网页(标签-安全|关键词-上传)
  • ¥35 lstm时间序列共享单车预测,loss值优化,参数优化算法
  • ¥15 Python中的request,如何使用ssr节点,通过代理requests网页。本人在泰国,需要用大陆ip才能玩网页游戏,合法合规。
  • ¥100 为什么这个恒流源电路不能恒流?
  • ¥15 有偿求跨组件数据流路径图
  • ¥15 写一个方法checkPerson,入参实体类Person,出参布尔值