完成第三章的书写

This commit is contained in:
astaxie
2012-09-05 16:11:37 +08:00
parent 50ae2dfd49
commit 6e11478f1a
6 changed files with 232 additions and 92 deletions

14
3.2.md
View File

@@ -1,6 +1,6 @@
#3.2 GO搭建一个web服务器
前面小节已经介绍了Web是基于http协议的一个服务Go语言里面提供了一个完善的net/http包通过http包可以很方便的就搭建起来一个可以运行的web服务。使用这个包能很简单地对web的路由静态文件模版cookie等数据进行设置和操作。
前面小节已经介绍了Web是基于http协议的一个服务Go语言里面提供了一个完善的net/http包通过http包可以很方便的就搭建起来一个可以运行的web服务。同时使用这个包能很简单地对web的路由静态文件模版cookie等数据进行设置和操作。
##http包建立web服务器
@@ -10,10 +10,11 @@
"fmt"
"net/http"
"strings"
"log"
)
func sayhelloName(w http.ResponseWriter, r *http.Request) {
r.ParseForm()
r.ParseForm() //解析参数,默认是不会解析的
fmt.Println(r.Form) //这些信息是输出到服务器端的打印信息
fmt.Println("path", r.URL.Path)
fmt.Println("scheme", r.URL.Scheme)
@@ -26,8 +27,11 @@
}
func main() {
http.HandleFunc("/", sayhelloName)
http.ListenAndServe(":9090", nil)
http.HandleFunc("/", sayhelloName) //设置访问的路由
err := http.ListenAndServe(":9090", nil) //设置监听的端口
if err != nil {
log.Fatal("ListenAndServe: ", err)
}
}
上面这个代码我们build之后然后执行web.exe,这个时候其实已经在9090端口监听tcp链接请求了。
@@ -52,7 +56,7 @@
>- 如果你以前是ruby程序员那么和ROR的/script/server启动有点类似。
我们看到我们通过简单的几行代码就已经运行起来一个web服务了而且这个web服务内部已经支持了高并发的特性我将会在接下来的两个小节里面详细的讲解一下go是如何实现web高并发的。
我们看到Go通过简单的几行代码就已经运行起来一个web服务了而且这个Web服务内部已经支持了高并发的特性我将会在接下来的两个小节里面详细的讲解一下go是如何实现Web高并发的。

134
3.3.md
View File

@@ -1,85 +1,49 @@
#3.3 Go如何使得web工作
前面小节介绍了如何通过Go搭建一个web服务我们可以看到简单的应用了一个net/http包就方便的搭建起来了。那么他底层到底是怎么做的呢但是万变不离其宗他离不开我们第一小节介绍的web工作方式。
##对应web工作方式的几个概念
Request用户请求的信息
Response服务器需要反馈给客户端的信息
Conn用户的每次请求链接
Handler处理请求和生成返回信息的处理逻辑
##分析http包运行机制
![](images/3.3.http.png?raw=true)
(1) 创建listen socket, 在指定的监听端口, 等待客户端请求的到来
(2) listen socket接受客户端的请求, 得到client socket, 接下来通过client socket与客户端通信
(3) 处理客户端的请求, 首先从client socket读取http请求的协议头, 如果是post协议, 还可能要
读取客户端上传的数据, 然后处理请求, 准备好客户端需要的数据, 通过client socket写给客户端
##web执行的过程分析
针对前一小节里面的代码我们来一行行的分析一下大概的http包里面执行流程应该是这样
- 首先调用Http.HandleFunc
按顺序做了几件事:
1 调用了DefaultServerMux的HandleFunc
2 调用了DefaultServerMux的Handle
3 往DefaultServeMux的map[string]muxEntry中增加对应的handler和路由规则
- 其次调用http.ListenAndServe(":12345", nil)
按顺序做了几件事情:
1 实例化Server
2 调用Server的ListenAndServe()
3 调用net.Listen("tcp", addr)监听端口
4 启动一个for循环在循环体中Accept请求
5 对每个请求实例化一个Conn并且开启一个goroutine为这个请求进行服务go c.serve()
6 读取每个请求的内容w, err := c.readRequest()
7 判断handler是否为空如果没有设置handler这个例子就没有设置handlerhandler就设置为DefaultServeMux
8 调用handler的ServeHttp
9 在这个例子中下面就进入到DefaultServerMux.ServeHttp
10 根据request选择handler并且进入到这个handler的ServeHTTP
mux.handler(r).ServeHTTP(w, r)
11 选择handler
A 判断是否有路由能满足这个request循环遍历ServerMux的muxEntry
B 如果有路由满足调用这个路由handler的ServeHttp
C 如果没有路由满足调用NotFoundHandler的ServeHttp
## links
* [目录](<preface.md>)
* 上一节: [GO搭建一个简单的web服务](<3.2.md>)
* 下一节: [Go的http包执行原理](<3.4.md>)
## LastModified
* $Id$
#3.3 Go如何使得Web工作
前面小节介绍了如何通过Go搭建一个Web服务我们可以看到简单的应用了一个net/http包就方便的搭建起来了。那么他底层到底是怎么做的呢但是万变不离其宗他离不开我们第一小节介绍的Web工作方式。
##对应web工作方式的几个概念
以下均是服务器端的相应概念
Request用户请求的信息用来解析用户的请求信息包括post、get、cookie、url等信息
Response服务器需要反馈给客户端的信息
Conn用户的每次请求链接
Handler处理请求和生成返回信息的处理逻辑
##分析http包运行机制
如下图所示是Go实现Web工作模式的流程图
![](images/3.3.http.png?raw=true)
(1) 创建listen socket, 在指定的端口监听, 等待客户端请求的到来
(2) listen socket接受客户端的请求, 得到client socket, 接下来通过client socket与客户端通信
(3) 处理客户端的请求, 首先从client socket读取http请求的协议头, 如果是post协议, 还可能要读取客户端上传的数据, 然后跟给相应的handler处理请求, 准备好客户端需要的数据, 通过client socket写给客户端
这整个的过程里面我们只要了解清楚下面三个问题也就知道Go是如何让Web运行起来了
- 如何监听端口?
- 如何接收客户端请求?
- 如何分配handler
前面小节的代码里面我们可以看到Go是通过一个函数来操作这个事情的`ListenAndServe`来监听起来的这个底层其实这样处理的初始化一个server对象然后调用了`net.Listen("tcp", addr)`也就是底层起的是TCP协议然后监控了我们设置的端口。
监控之后如何接收客户端的请求呢?上面的监控端口之后,就调用了`srv.Serve(net.Listener)`函数,这个函数就是处理接收客户端的请求信息。这个函数里面起了一个`for{}`里面通过Listener接收请求然后起一个Conn然后单独开了一个goroutine把这个请求的数据当做参数扔给这个conn去服务`go c.serve()`。这个就是高并发体现了用户来的请求都是goroutine去服务相互不影响。
那么如何具体分配到相应的函数来处理请求呢conn首先会解析request:`c.readRequest()`,然后获取相应的handler:`handler := c.server.Handler`,也就是我们刚才在调用函数`ListenAndServe`时候的第二个参数我们前面例子传递的是nil也就是为空那么默认获取`handler = DefaultServeMux`,那么这个是哪里来的东西呢这个东西就是路由器也就是把相应的请求url对应请求函数的路由器那么这个我们有设置过吗?有,我们调用的代码里面第一句不是调用了`http.HandleFunc("/", sayhelloName)`嘛。这个就是注册了相应的路由url为"/"的请求到函数sayhelloNameDefaultServeMux会调用ServeHTTP方法这个方法内部其实就是调用sayhelloName本身最后通过写入response的信息反馈到客户端。
至此我们的三个问题已经全部得到了解答你现在对于Go如何让Web跑起来的是否已经大概清楚了呢
## links
* [目录](<preface.md>)
* 上一节: [GO搭建一个简单的web服务](<3.2.md>)
* 下一节: [Go的http包详解](<3.4.md>)
## LastModified
* $Id$

160
3.4.md Normal file
View File

@@ -0,0 +1,160 @@
#3.4 Go的http包详解
前面小节介绍了Go怎么样实现了Web工作模式的一个流程这一小节我们讲来详细的解剖一下http包他到底怎么样实现整个的过程的。
Go的http有两个核心功能Conn、ServeMux
##Conn的goroutine
与我们一般编写的http服务器不同, Go为了实现高并发和高性能, 使用了goroutines来处理Conn的读写事件, 这样每个请求都能保持独立相互不会阻塞可以高效的响应网络事件。这是Go高效的保证。
Go在等待客户端请求里面是这样写的
c, err := srv.newConn(rw)
if err != nil {
continue
}
go c.serve()
这里我们可以看到客户端的每次请求都会创建一个Conn这个Conn里面保存了该次请求的信息然后再传递到handler的时候可以读取到相应的header信息这样保证了每个请求的独立性。
##ServeMux的自定义
我们前面小节讲述conn.server的时候其实内部是调用了http包默认的路由器通过路由器把本次请求的信息传递到了后端的处理函数。那么这个路由器是怎么实现的呢
它的结构如下:
type ServeMux struct {
mu sync.RWMutex //锁,由于请求设计到并发处理,因此这里需要一个锁机制
m map[string]muxEntry // 路由规则一个string对应一个mux实体这里的string就是注册的路由表达式
}
下面看一下muxEntry
type muxEntry struct {
explicit bool // 是否精确匹配
h Handler // 这个路由表达式对应哪个handler
}
下面看一下handler的定义
type Handler interface {
ServeHTTP(ResponseWriter, *Request) // 路由实现器
}
handler是一个接口但是我们定义的函数`sayhelloName`没有实现ServeHTTP这个接口为什么能添加呢原来在http包里面还定义了一个类型`HandlerFunc`,我们定义的函数`sayhelloName`就是这个handlerFunc调用之后的结果这个类型默认就实现了ServeHTTP这个接口即我们调用了HandlerFunc(f),类似强制类型转换f成为handlerFunc类型这样f就拥有了ServHTTP方法。
type HandlerFunc func(ResponseWriter, *Request)
// ServeHTTP calls f(w, r).
func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {
f(w, r)
}
路由器里面存储好了相应的路由规则之后,那么具体的请求又是怎么分发的呢?
路由器接收到请求之后调用`mux.handler(r).ServeHTTP(w, r)`
也就是调用对应路由的handler的ServerHTTP接口那么mux.handler(r)怎么处理的呢?
func (mux *ServeMux) handler(r *Request) Handler {
mux.mu.RLock()
defer mux.mu.RUnlock()
// Host-specific pattern takes precedence over generic ones
h := mux.match(r.Host + r.URL.Path)
if h == nil {
h = mux.match(r.URL.Path)
}
if h == nil {
h = NotFoundHandler()
}
return h
}
原来他是根据用户请求的URL和路由器里面存储的map去匹配的当匹配到之后返回存储的handler然后handler实现了ServHTTP接口调用这个ServHTTP就可以执行到相应的函数了。
通过上面这个介绍我们了解了路由器的整个执行过程Go其实支持外部实现的路由器因为我们在调用`ListenAndServe`的时候第二个参数就是Handler只要实现了ServHTTP函数就可以。
如下代码所示,我们自己实现了一个简易的路由器
package main
import (
"fmt"
"net/http"
)
type MyMux struct {
}
func (p *MyMux) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if r.URL.Path == "/" {
sayhelloName(w, r)
return
}
http.NotFound(w, r)
return
}
func sayhelloName(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello myroute!")
}
func main() {
mux := &MyMux{}
http.ListenAndServe(":9090", mux)
}
##Go代码的执行流程
通过对http包的分析之后现在让我们来梳理一下整个的代码执行过程。
- 首先调用Http.HandleFunc
按顺序做了几件事:
1 调用了DefaultServerMux的HandleFunc
2 调用了DefaultServerMux的Handle
3 往DefaultServeMux的map[string]muxEntry中增加对应的handler和路由规则
- 其次调用http.ListenAndServe(":9090", nil)
按顺序做了几件事情:
1 实例化Server
2 调用Server的ListenAndServe()
3 调用net.Listen("tcp", addr)监听端口
4 启动一个for循环在循环体中Accept请求
5 对每个请求实例化一个Conn并且开启一个goroutine为这个请求进行服务go c.serve()
6 读取每个请求的内容w, err := c.readRequest()
7 判断handler是否为空如果没有设置handler这个例子就没有设置handlerhandler就设置为DefaultServeMux
8 调用handler的ServeHttp
9 在这个例子中下面就进入到DefaultServerMux.ServeHttp
10 根据request选择handler并且进入到这个handler的ServeHTTP
mux.handler(r).ServeHTTP(w, r)
11 选择handler
A 判断是否有路由能满足这个request循环遍历ServerMux的muxEntry
B 如果有路由满足调用这个路由handler的ServeHttp
C 如果没有路由满足调用NotFoundHandler的ServeHttp
## links
* [目录](<preface.md>)
* 上一节: [Go如何使得web工作](<3.3.md>)
* 下一节: [小节](<3.5.md>)
## LastModified
* $Id$

12
3.5.md Normal file
View File

@@ -0,0 +1,12 @@
#3.5小节
这一章我们通过介绍Web工作方式介绍了HTTP协议的相关信息通过类比引出了Go怎么运行Web实现了Go的web服务器通过对Go运行Web的代码进行分析深入了解了Go执行Web的整个流程最后我们对Go语言的http包进行了详细的分析。
通过这一章的学习我希望你能够对Go开发Web有了初步的了解我们也看到相应的代码了这个开发是很方便的同时又是相当的灵活。
## links
* [目录](<preface.md>)
* 上一节: [Go的http包详解](<3.4.md>)
* 下一章: [小节](<4.md>)
## LastModified
* $Id$

2
3.md
View File

@@ -4,7 +4,7 @@
* 1. [web工作方式](3.1.md)
* 2. [GO搭建一个简单的web服务](3.2.md)
* 3. [Go如何使得web工作](3.3.md)
* 4. [Go的http包执行原理](3.4.md)
* 4. [Go的http包详解](3.4.md)
* 5. [小结](3.5.md)
学习基于Web的编程可能正是你读本书的原因。事实上如何通过Go来编写Web应用也是我编写这本书的初衷。前面已经介绍过Go目前已经拥有了成熟的Http处理包这使得编写能做任何事情的动态Web程序易如反掌。在接下来的各章中将要介绍的内容都是属于Web编程的范畴。本章则集中讨论一些与Web相关的概念和Go如何运行Web程序的话题。

View File

@@ -17,7 +17,7 @@
- 3.1 [web工作方式](3.1.md)
- 3.2 [GO搭建一个简单的web服务](3.2.md)
- 3.3 [Go如何使得web工作](3.3.md)
- 3.4 [Go的http包执行原理](3.4.md)
- 3.4 [Go的http包详解](3.4.md)
- 3.5 [小结](3.5.md)
* 4.表单
- 4.1 处理表单的输入