Go:http服务探究

在单核CPU的情况下,实现一个返回”Hello World”的服务器为例,通过比较C与Go的代码,探究go的一些设计与理念。

如果,以C语言为例,要实现一个返回”Hello World”的http服务器。

1. Listen端口
2. 为了支持高并发,将fd加入 加入epoll监听。
3. 该fd可读,accept 新的fd. 并开始监听。
4. 读取新连接的数据(二进制流)
5. http协议的分析处理
6. 向新连接写入http协议二进制流"hello World"。

主要还是 listen,accept,send几个api.

看一下go的实现。下面的go代码也实现了上述的效果。

package main
import (
    "fmt"
    "net/http"
)

func hello(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "Hello, world \n")
}

func main() {
    http.HandleFunc("/", hello)
    err := http.ListenAndServe(":8080", nil)
    if err != nil {
        fmt.Println("return")
    }
}

通过追代码,有以下几个感悟:

1. Go 中listen的部分。
定义一个Listener的接口。
type Listener interface {
        Accept() (Conn, error)
        Close() error
        Addr() Addr
}

在上述的代码中,ListenAndServe中,使用了下面的结构。
type tcpKeepAliveListener struct {
    *net.TCPListener
}

通过对TCPListener的包装,继承了该结构体的其他函数,只是重写accept() 函数。
从逻辑上说只是一层包装,实现了从该fd accept的连接 增加keepalive的功能。
2. Server结构。

// handler接口,定义一个可处理http请求 r, 并写回 w 中的函数。
// 其实,看代码就可以知道 r 就是从 w 中读出来的。

type Handler interface {
    ServeHTTP(w ResponseWriter, r *Request)
}

type HandlerFunc func(ResponseWriter, *Request) 
func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {
    f(w, r)
}



func (srv *Server) Serve(l net.Listener) {
    // 函数的参数是listener接口类型。
    
    rw = l.accept()
    // A conn represents the server side of an HTTP connection.
    c := srv.newConn(rw)
    c.setState(c.rwc, StateNew) // before Serve can return
    // go 高并发的秘诀就在这里,每一个accept 使用一个go程处理。
    // 和epoll 有异曲同工之妙。
    // 其实底子里,go 还是用的 epoll.
    go c.serve(ctx)
}
3. conn是一个结构体,表示http Server端的一个连接。

它会调用Server中的 handler, 也就是我们的 hello 函数。

func (c *conn) serve(ctx context.Context) {
    serverHandler{c.server}.ServeHTTP(w, w.req)
}

总结:

本来是想探索下go的实现中,接口设计相关的东西,但是代码看下来,只达到了窥一斑。

7 Common Mistakes

interface只是行为,behavior。function 不改变状态,same input, same output.所以说 它只是在做事情。

struct包括 state 和 methods. methods 会改变结构体本身的状态。

还需要更多的学习,加深体会。