简单的Go Web服务器理解

  • 作者:卡牌
  • 时间:2019-02-24
  • 阅读量:129

net/http

1.http包提供了HTTP客户端和服务端的实现。

a.客户端

Get、Head、Post和PostForm函数发出HTTP/ HTTPS请求。

resp, err := http.Get("http://example.com/")
...
resp, err := http.Post("http://example.com/upload", "image/jpeg", &buf)
...
resp, err := http.PostForm("http://example.com/form",
    url.Values{"key": {"Value"}, "id": {"123"}})

程序在使用完回复后必须关闭回复的主体

defer resp.Body.Close()
// 读取请求网址内容
body, err := ioutil.ReadAll(resp.Body)
...

在使用爬虫的时候,由于网站屏蔽爬虫,resp.StatusCode为403等,所以必须要模拟成浏览器

需要管理HTTP客户端的头域、重定向策略和其他设置,创建一个client,如下例子

client := &http.Client{}

req, err := http.NewRequest("GET", "www.example.com", nil)

if err != nil {
    panic(err)
}

# 设置请求头
 req.Header.set("user-Agent", 
    
    "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) 

    Chrome/66.0.3359.181 Safari/537.36")

resp, err := client.Do(req)
if err != nil {
    panic(err)
}
defer resp.Body.Close()

要管理代理、TLS配置、keep-alive和其他设置,创建一个Transport

Client和Transport类型都可以安全的被多个go程同时使用。

出于效率考虑,应该一次建立、尽量重用。

a.服务端

ListenAndServe使用指定的监听地址和处理器启动一个HTTP服务端。

处理器参数通常是nil,这表示采用包变量DefaultServeMux作为处理器。

Handle和HandleFunc函数可以向DefaultServeMux添加处理器。

示例如下:

func main() {
    http.HandleFunc("/", hello) // 设置访问的路由
    err := http.ListenAndServe(":8080", nil) // 设置监听TCP的端口
    if err != nil {
        log.Fatal("ListenAndServe: ", err)
    }
}

func hello(w http.ResponseWriter, r *http.Request) {
    # r代表Request请求对象,可以获取所有请求内容
    # w代表返回对象
    fmt.Println(r.Form) //这些信息是输出到服务器端的打印信息
    fmt.Println("path", r.URL.Path)
    fmt.Println("scheme", r.URL.Scheme)
    w.Write([]byte("hello world"))
}

c.Web工作方式的几个概念

以下几个为服务器段的概念

  1. Request:用户请求的信息,用来解析用户的请求信息,包括post、get、cookie、url等信息
  2. Response:服务器需要反馈给客户端的信息
  3. Conn:用户的每次请求链接
  4. Handler:处理请求和生成返回信息的处理逻辑

d.http包运行机制

6624280-33173c036c3037b6.png

这个过程我们需要清楚以下三个问题,则就清楚Go是如何让Web运行起来了

如何监听端口?

通过上面的代码我们看到Go是通过一个函数ListenAndServe来处理这些事情的,

这个底层其实这样处理的:

初始化一个server对象,然后调用了net.Listen("tcp", addr),

也就是底层用TCP协议搭建了一个服务,然后监控我们设置的端口。

如何接收客户端请求?

源码执行监控端口之后

ln, err := net.Listen("tcp", addr)

调用了srv.Serve(net.Listener)函数,这个函数就是处理接收客户端的请求信息。

return srv.Serve(tcpKeepAliveListener{ln.(*net.TCPListener)})

这个函数里面起了一个for{},

for {
    rw, e := l.Accept() # Listener接收请求,
    ...
}

其次创建一个 Conn,

c := srv.newConn(rw)

最后单独开了一个 goroutine,把这个请求的数据当做参数扔给这个conn去服务:

go c.serve()

这个就是高并发体现了, 用户的每一次请求都是在一个新的goroutine去服务,相互不影响

如何分配handler

conn首先会解析

request:c.readRequest()

然后获取相应的

handler:handler := c.server.Handler,

也就是我们刚才在调用函数ListenAndServe时候的第二个参数,我们前面例子传递的是nil,

也就是为空,那么默认获取handler = DefaultServeMux,那么这个变量用来做什么的呢?

对,这个变量就是一个路由器,它用来匹配url跳转到其相应的handle函数,

那么这个我们有设置过吗?有,我们调用的代码里面第一句不是调用了

http.HandleFunc("/", hello)嘛。这个作用就是注册了请求/的路由规则,

当请求uri为"/",路由就会转到函数hello,

DefaultServeMux会调用ServeHTTP方法,这个方法内部其实就是调用hello本身,

最后通过写入response的信息反馈到客户端。

6624280-015743db004f40d8.png

可以简单理解为

Clinet -> Requests ->  [Multiplexer(router) -> handler  -> Response -> Clinet

上一篇: elasticSearch入门

下一篇: MYSQL语句执行流程

文章评论

共有0条评论来说两句吧...

提交
Top