Go Web应用程序+;MySql驱动程序暂停/超时
更新:进一步诊断后,go sql驱动程序/mysql I驱动程序包出现问题。事实证明,底层tcp似乎无法检测到断开的tcp连接。完整的详细信息在下面的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-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
// 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, ®ion, ®ion_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_dateNeverdefer 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
}