完成了第二小节,第三小节开了一个头

This commit is contained in:
xiemengjun
2012-11-20 23:43:15 +08:00
parent b2d8306857
commit 80c847a122
2 changed files with 106 additions and 7 deletions

104
12.2.md
View File

@@ -16,17 +16,107 @@
- 网络出错:指两方便的错误,一方面是用户请求应用程序的时候出现网络断开,这样就导致连接中断,这种错误不会造成应用程序的崩溃,但是会影响用户访问的效果,另一方面是应用程序读取其他网络上的数据,这种网络断开会导致读取失败,这种需要对应用程序做有效的测试,能够避免这类问题出现的情况下程序崩溃。
## 错误处理的目标
在实现错误处理之前,我们必须明确错误处理想要达到的目标是什么,错误处理系统应该完成下工作:
在实现错误处理之前,我们必须明确错误处理想要达到的目标是什么,错误处理系统应该完成下工作:
- 通知访问用户出现错误了
- 记录错误
- 回滚当前的请求
- 根据错误级别发送邮件
- 保证现有程序可运行可服务
- 通知访问用户出现错误了不论出现的是一个系统错误还是用户错误用户都应当知道Web应用出了问题用户的这次请求无法正确的完成了。例如用户的错误请求我们显示一个统一的错误页面(404.html),出现系统错误我们通过自定义的错误页面显示系统暂时不可用之类的错误页面(error.html)
- 记录错误系统出错错误时一般就是我们调用函数的时候返回err不为nil的情况下使用前面小节介绍的日志系统记录到日志文件如果是一些致命错误通过邮件通知系统管理员例如一般404之类的错误不需要发送邮件只需要记录到日志系统。
- 回滚当前的请求:如果一个用户请求过程中出现了一个服务器错误,那么已完成的操作需要回滚。下面来看一个例子:一个系统讲用户递交的表单保存到数据库,并将这个数据递交到一个第三方服务器,但是第三方服务器挂了,这就导致一个错误,那么先前存储到数据库的表单数据应该删除,而且应该通知用户系统出现错误了。
- 保证现有程序可运行可服务:我们知道没有人能保证程序一定能够一直正常的运行着,万一哪一天程序崩溃了,那么我们就需要记录错误,然后立刻让程序重新运行起来,让程序继续提供服务,然后再通知系统管理员,通过日志等找出问题。
## 如何处理错误
错误处理其实我们已经在十一章第一小节里面有过介绍如何设计错误处理,这里我们再从一个例子详细的讲解一下,如何来处理不同的错误:
- 通知用户出现错误:
通知用户在访问页面的时候我们可以有两种错误404.html和error.html下面分别显示了错误页面的源码
<html lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>找不到页面</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>
<body>
<div class="container">
<div class="row">
<div class="span10">
<div class="hero-unit">
<h1>404!</h1>
<p>{{.ErrorInfo}}</p>
</div>
</div><!--/span-->
</div>
</div>
</body>
</html>
另一个源码:
<html lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>系统错误页面</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>
<body>
<div class="container">
<div class="row">
<div class="span10">
<div class="hero-unit">
<h1>系统暂时不可用!</h1>
<p>{{.ErrorInfo}}</p>
</div>
</div><!--/span-->
</div>
</div>
</body>
</html>
404的错误处理逻辑如果是系统的错误也是类似的操作同时我们看到在
func (p *MyMux) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if r.URL.Path == "/" {
sayhelloName(w, r)
return
}
NotFound404(w, r)
return
}
func NotFound404(w http.ResponseWriter, r *http.Request) {
log.Error("页面找不到") //记录错误日志
t, _ = t.ParseFiles("tmpl/404.html", nil) //解析模板文件
ErrorInfo := "文件找不到" //获取当前用户信息
t.Execute(w, ErrorInfo) //执行模板的merger操作
}
func SystemError(w http.ResponseWriter, r *http.Request) {
log.Critical("系统错误") //系统错误触发了Critical那么不仅会记录日志还会发送邮件
t, _ = t.ParseFiles("tmpl/error.html", nil) //解析模板文件
ErrorInfo := "系统暂时不可用" //获取当前用户信息
t.Execute(w, ErrorInfo) //执行模板的merger操作
}
## 如何处理异常
我们知道在很多其他语言中有try...catch操作可以捕获异常情况但是其实很多的错误我们都是可以预期的而不是异常处理应该当做错误来处理这也是为什么Go语言采用了函数返回错误的设计这些函数不会panic例如如果一个文件找不到os.Open返回一个错误它不会panic如果你向一个中断的网络连接写数据net.Conn系列类型的Write函数返回一个错误它们不会panic。这些状态在这样的程序里都是可以预期的。你知道这些操作可能会失败因为设计者已经用返回错误清楚地表明了这一点。这就是上面所讲的可以预期的错误。
但是还有一种情况有一些操作几乎不可能失败而且在一些特定的情况下也没有办法返回错误也无法继续执行这样情况就应该panic。举个例子如果一个程序计算x[j]但是j越界了这部分代码就会导致panic像这样的一个不可预期严重错误就会引起panic在默认情况下它会杀掉进程它允许一个正在运行这部分代码的goroutine从发生错误的panic中恢复运行发生panic之后这部分代码后面的函数和代码都不会继续执行这是Go特意这样设计的因为要区别于错误和异常panic其实就是异常处理。如下代码我们期望通过uid来获取User中的username信息但是如果uid越界了就会抛出异常这个时候如果我们没有recover机制进程就会被杀死从而导致程序不可服务。因此为了我们程序的健壮性在一些地方我们需要建立recover机制。
func GetUser(uid int) (username string) {
defer func() {
if x := recover(); x != nil {
username = ""
}
}()
username = User[uid]
return
}
上面我们介绍了错误和异常的区别那么我们在开发程序的时候如何来设计呢规则很简单如果你的函数无论如何有可能失败它就应该返回一个错误。当我调用其他package的函数时如果这个函数实现的很好我不需要担心它会panic除非有真正的异常情况发生即使那样也不应该是我去处理它。而panic和recover是针对自己开发package里面实现的逻辑针对一些特殊情况来设计。
## 小结
本小节总结了当我们的Web应用部署之后如何处理各种错误网络错误、数据库错误、操作系统错误等当错误发生时我们的程序如何来正确的处理显示友好的出错界面、回滚操作、记录日志、通知管理员等操作最后介绍了如何来正确的处理错误和异常一般的程序中错误和异常都是混淆的但是在Go程序中错误和异常是有区分的所以最后介绍了程序设计的时候如何来遵循这样的原则。
## links
* [目录](<preface.md>)
* 上一章: [应用日志](<12.1.md>)

View File

@@ -1,6 +1,15 @@
# 12.3 应用部署
程序开发完毕之后我们现在要部署Web应用程序了但是我们如何来部署这些应用程序呢因为Go程序编译之后是一个可执行文件编写过C程序的读者一定知道使用demon就可以完美的实现程序后台运行但是目前Go还无法完美的实现demon因此针对Go的应用程序部署我们可以利用第三方工具来管理第三方的工具有很多例如Supervisord、upstart、daemontools等这小节我介绍目前自己系统中采用的工具Supervisord。
## deamon
我们可以看到很多网上的一些实现demon的方法例如下面两种方式
http://code.google.com/p/go/issues/detail?id=227
## Supervisord
Supervisord是用Python实现的一款非常实用的进程管理工具。
## 小结
## links
* [目录](<preface.md>)