Go Web应用程序+;MySql驱动程序暂停/超时

Go Web应用程序+;MySql驱动程序暂停/超时,mysql,go,Mysql,Go,更新:进一步诊断后,go sql驱动程序/mysql I驱动程序包出现问题。事实证明,底层tcp似乎无法检测到断开的tcp连接。完整的详细信息在下面的go-sql-driver/mysql项目的github版本中: -- 在空闲15到48分钟后,我当前遇到一个web应用程序暂停或损坏。最关键的问题如下所述: 访问一个URL,站点上的任何URL,并完全加载页面(如中所示,页面实际加载,日志显示已加载完整页面) 关闭浏览器,然后等待 典型的请求记录如下: 2014/07/13 15:29:54

更新:进一步诊断后,go sql驱动程序/mysql I驱动程序包出现问题。事实证明,底层tcp似乎无法检测到断开的tcp连接。完整的详细信息在下面的
go-sql-driver/mysql
项目的github版本中:

--

在空闲15到48分钟后,我当前遇到一个web应用程序暂停或损坏。最关键的问题如下所述:

  • 访问一个URL,站点上的任何URL,并完全加载页面(如中所示,页面实际加载,日志显示已加载完整页面)
  • 关闭浏览器,然后等待
典型的请求记录如下:

2014/07/13 15:29:54 INFO template rendering: index
2014/07/13 15:29:54 METRIC, URL: /, HANDLER TIME: 7.2339ms, CTX TIME: 5.0894ms, TOTAL TIME: 12.3258ms
经过很长一段时间(从15米到48米),系统突然在没有交互的情况下记录下面的这些行-web应用程序一直处于空闲状态:

[MySQL] 2014/07/13 16:00:09 packets.go:32: read tcp remote-mysql-server-address:3306: connection timed out
[MySQL] 2014/07/13 16:00:09 packets.go:118: write tcp remote-mysql-server-address:3306: broken pipe
2014/07/13 16:00:10 INFO template rendering: index
2014/07/13 16:00:10 METRIC, URL: /, HANDLER TIME: 8.8574ms, CTX TIME: 31m19.2606723s, TOTAL TIME: 31m19.2695329s
注意“总时间”是31分19秒?另外,请注意同时记录的MySql驱动程序错误

没有活动/没有提出web请求。该web应用程序只是处于空闲状态

最关键的问题是这些日志消息之后的下一个问题:下一个web请求完全暂停,从不返回响应:

user@govm1:~$ wget http://localhost
--2014-07-13 17:11:18--  http://localhost/
Resolving localhost (localhost)... 127.0.0.1
Connecting to localhost (localhost)|127.0.0.1|:80... connected.
HTTP request sent, awaiting response... Read error (Connection timed out) in headers.
Retrying.

--2014-07-13 17:26:19--  (try: 2)  http://localhost/
Connecting to localhost (localhost)|127.0.0.1|:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: unspecified [text/html]
Saving to: `index.html.4'

    [ <=>                                                                                  ] 6,310       --.-K/s   in 0.001s

2014-07-13 17:26:20 (9.61 MB/s) - `index.html.4' saved [6310]
再等15分钟

我将运行go web应用程序的群集VIP和防火墙/Linux虚拟机Windows Azure作为一个问题删除,因为我运行了
wgethttp://localhost
本地在同一个框中,我得到这个“暂停”的请求,它永远不会完成,也不会返回任何内容

--

在我的web应用程序中有许多因素,因此我将尝试相应地概述它们

使用:

  • 加油1.3
  • go sql驱动程序/mysql版本1.2(2014-06-03)
  • Ubuntu 12.04 LTS,2014年6月更新
  • WindowsAzure
请注意,运行MySql的Linux设备与运行GoLang应用集群的Linux设备不同,它们位于单独的专用云服务中。MySql虚拟机是一个单一的虚拟机,没有集群

以下是一些相关代码:

// global handler for our DB
var db *sql.DB

// CLI parameter
var dbdsn string

func init() {

    flag.StringVar(&dbdsn, "dbdsn", "root:root@tcp(localhost:3306)/prod?timeout=5s&tls=false&autocommit=true", "Specifies the MySql DSN connection.")
    flag.Parse()

    var err error
    db, err = sql.Open("mysql", dbdsn)
    if err != nil {
        log.Printf("ERROR in sql.Open(): %v", err)
    }

    //db.SetMaxIdleConns(5)

    // verify the DSN is setup properly1
    err = db.Ping()
    if err != nil {
        panic("PANIC when pinging db: " + err.Error()) // proper error handling instead of panic in your app
    }
}

// **********
// * omitted is the Gorilla MUX router and http handler registrations
// **********

func ArticleHandler(w http.ResponseWriter, r *http.Request, c *Context) (err error) {

    m := NewArticle(c)
    id := c.Vars["id"]

    var pid int
    var title, body, excerpt, date, slug, fi, fv, region, region_slug string
    err = db.QueryRow(
        "SELECT p.ID, p.post_title, p.post_content, p.post_excerpt, p.post_date, p.post_name, "+
            "(SELECT fpim.meta_value FROM wp_postmeta fpim WHERE fpim.meta_key = '_wp_attached_file' AND fpim.post_id = (SELECT fpim2.meta_value FROM wp_postmeta fpim2 WHERE fpim2.post_id = p.ID AND fpim2.meta_key = '_thumbnail_id' LIMIT 1) LIMIT 1) AS featured_image, "+
            "(SELECT fpim3.meta_value FROM wp_postmeta fpim3 WHERE fpim3.meta_key = 'fv_video' AND fpim3.post_id = p.ID LIMIT 1) AS featured_video, "+
            "t.name as region, t.slug as region_slug "+
            "FROM wp_posts p "+
            "JOIN wp_term_relationships tr ON tr.object_id=p.ID "+
            "JOIN wp_term_taxonomy tt ON tt.term_taxonomy_id=tr.term_taxonomy_id "+
            "JOIN wp_terms t ON t.term_id=tt.term_id "+
            "WHERE p.post_name=? AND p.post_type='post' AND p.post_status='publish' AND p.post_date <= UTC_TIMESTAMP()"+
            "AND tr.object_id=p.ID AND tt.parent = (SELECT t3.term_id FROM wp_terms t3 WHERE t3.name=? LIMIT 1) LIMIT 1",
        id, RegionsParentCategory).
        Scan(&pid, &title, &body, &excerpt, &date, &slug, &fi, &fv, &region, &region_slug)

    if err != nil {
        if err == sql.ErrNoRows {

            // snipped code for redirects

            // article was not found
            return handleNotFound(w, r, c)

        } else {
            log.Printf("ERROR in .Scan(): %v", err)
        }
    } else {
        m.Region = Region{
            Name: region,
            Slug: region_slug,
        }
        m.Id = pid
        m.Title = title
        m.Body = template.HTML(body) // render the raw html
        m.Excerpt = excerpt
        m.Datetime = date
        m.Slug = slug
        m.FeaturedImageUrl = fi
        m.FeaturedVideoUrl = fv
    }

    web.RenderTemplate(w, "article", m)
    return
}
没什么特别的

只有在没有错误的情况下,我才会调用
defer rows2.Close()
。也许这就是问题的一部分?这个特定的SQL查询似乎将负载测试下的错误记录为
无响应
或mysql驱动程序超时

问题 为什么在空闲站点上记录的请求超时超过15到30分钟?这似乎是我正在使用的mysql驱动程序的一个错误,可能是连接处于打开状态。但是,最后一个http请求成功并返回了完整的page+模板

我甚至在连接字符串中设置了超时,即5秒。即使mysql服务器有问题,为什么还要记录15分钟的超时/请求?这个要求是从哪里来的

这仍然可能是一个MySql驱动程序问题,阻止了请求的完成——可能是被MySql专用VM阻止了,这是一个问题。如果是这样的话,为什么什么都不记录?15到4900万分钟的随机超时是什么?通常只有15米或31米,但有时记录为48米

超时(@15m、31m和48m)中的“15m”倍数非常有趣,在几秒钟内分配一些填充。

提前谢谢

我应该在每个web请求/处理程序上“打开”并延迟db.Close()吗

不可以。创建一个全局数据库,不用担心关闭它(就像现在一样),或者通过应用程序上下文传递池(
*sql.DB
),也就是说,通过在嵌入
*sql.DB
和其他任何您可能需要的上下文类型上使用处理程序作为方法

您可能还想看看如何使用来帮助将数据库结果封送到结构/映射中,而不必自己跳这个舞


顺便说一句(我敢打赌,这将有助于调试问题),我会修复您的第二个代码示例以匹配以下内容-因为当您遇到错误时,您只记录它,但您的代码会继续,因为您不会返回:

rows2, err := db.Query(`
    SELECT p.post_title, p.post_name
    FROM wp_posts p 
    WHERE p.post_type='page' AND p.post_status='publish' AND p.post_date <= UTC_TIMESTAMP()
    AND p.post_parent = (SELECT p2.ID FROM wp_posts p2 WHERE p2.post_name=? LIMIT 1)
    ORDER BY p.menu_order`, FooterPagesParentNameSlug)
if err != nil {
    log.Printf("ERROR in AllPages .Query() : %v", err)
    return err
}
defer rows2.Close()

c.AllFooterPages = make([]FooterPage, 0)

for rows2.Next() {
    var name, slug string
    err := rows2.Scan(&name, &slug)
    if err != nil {
        log.Printf("ERROR in AllPages row.Scan() : %v", err)
        return err // Same here!
    }
// Rest of your code
}
rows2,err:=db.Query(`
选择p.post\U标题、p.post\U名称
来自wp_posts p
其中,init.init中的p.post_type='page'和p.post_status='publish'以及p.post_dateNever
defer db.Close()
在main执行之前结束,因此永远无法访问打开的连接池。 不过,您可以在
main
中调用
defer db.Close()

这可能也是您准备的语句的问题,它们属于连接池,在调用
db.Close()
时无效

关于超时,这是一个驱动端超时(从问题中获得)


请在

上查看我的评论,谢谢。实际上,我对每个要查看的请求都显式地执行了sql.Open()和db.Close()。在相同的15米到31米间隔后,我出现了与上面列出的相同的错误。最初的问题是我遇到的连接超时/管道断开的问题。因此,我无法接受这一问题作为答案。对不起,您的最后的“问题”部分没有再次提到超时。您可能应该重构以删除多余的“如果有”"块-如果
err!=nil
则提前返回,或者继续-不需要其他。当您说超时在同一个查询上时,您的日志语句是否写入了任何内容?如果是,是哪一个错误?需要更多信息。感谢您提供返回错误的附加代码。我特意决定不返回错误并继续让页面呈现,这样用户就不会在屏幕上出现错误。这是Go的功能之一:它强制您显式地处理错误。因此,我选择不返回错误-只记录错误。代码很合理,但不会出现任何nil字段错误。如果您尝试打开/关闭每个请求,但仍然发生了这种情况,那么这就是在服务器上发生的情况下,99%确定这与G无关
rows2, err := db.Query(
    "SELECT p.post_title, p.post_name "+
        "FROM wp_posts p "+
        "WHERE p.post_type='page' AND p.post_status='publish' AND p.post_date <= UTC_TIMESTAMP() "+
        "AND p.post_parent = (SELECT p2.ID FROM wp_posts p2 WHERE p2.post_name=? LIMIT 1) "+
        "ORDER BY p.menu_order",
    FooterPagesParentNameSlug)
if err != nil {
    log.Printf("ERROR in AllPages .Query() : %v", err)
} else {
    defer rows2.Close()
    c.AllFooterPages = make([]FooterPage, 0)
    for rows2.Next() {
        var name, slug string
        err := rows2.Scan(&name, &slug)
        if err != nil {
            log.Printf("ERROR in AllPages row.Scan() : %v", err)
        } else {
            p := FooterPage{
                Page: Page{
                    Title: name,
                    Slug:  slug,
                },
            }
            c.AllFooterPages = append(c.AllFooterPages, p)
        }
    }
}
rows2, err := db.Query(`
    SELECT p.post_title, p.post_name
    FROM wp_posts p 
    WHERE p.post_type='page' AND p.post_status='publish' AND p.post_date <= UTC_TIMESTAMP()
    AND p.post_parent = (SELECT p2.ID FROM wp_posts p2 WHERE p2.post_name=? LIMIT 1)
    ORDER BY p.menu_order`, FooterPagesParentNameSlug)
if err != nil {
    log.Printf("ERROR in AllPages .Query() : %v", err)
    return err
}
defer rows2.Close()

c.AllFooterPages = make([]FooterPage, 0)

for rows2.Next() {
    var name, slug string
    err := rows2.Scan(&name, &slug)
    if err != nil {
        log.Printf("ERROR in AllPages row.Scan() : %v", err)
        return err // Same here!
    }
// Rest of your code
}