HTTP服务
对于C/S模式,HTTP服务主要关注客户端请求和服务端应答。客户端与服务器建立 TCP 连接后,客户端发出请求(request)给服务端,服务端接收到请求,通过处理,进行应答(response)。
使用go语言开发简单的 web 服务程序
例:
// main.go
package main
import (
"fmt"
"log"
"net/http"
)
func sayhelloName(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello!") // 这个写入到w的是输出到客户端的
}
func main() {
http.HandleFunc("/", sayhelloName) // 设置访问的路由
port := ":8080" // 设置监听的端口
err := http.ListenAndServe(port, nil) // 开始监听
if err != nil {
log.Fatal("ListenAndServe: ", err) // 输出错误日志
}
fmt.Printf("Server listen at: %s", port)
}
只需在终端运行命令:
$ go run main.go
使用go语言实现一个简单的http server,我们只需要import net/http包,就可以解析http客户端的请求。
部分源码分析
HandleFunc() :注册路由
http.HandleFunc("/", IndexHandler)
HandleFunc函数用于注册路由:
// HandleFunc registers the handler function for the given pattern
// in the DefaultServeMux.
// The documentation for ServeMux explains how patterns are matched.
func HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
DefaultServeMux.HandleFunc(pattern, handler)
}
这一步是调用实例化对象DefaultServerMux的HandleFunc函数
DefaultServerMux:
// DefaultServeMux is the default ServeMux used by Serve.
var DefaultServeMux = &defaultServeMux
var defaultServeMux ServeMux
type ServeMux struct {
mu sync.RWMutex
m map[string]muxEntry
hosts bool // whether any patterns contain hostnames
}
type muxEntry struct {
h Handler
pattern string
}
HandleFunc函数:
// HandleFunc registers the handler function for the given pattern.
func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
if handler == nil {
panic("http: nil handler")
}
mux.Handle(pattern, HandlerFunc(handler))
}
HandleFunc函数会继续调用mux.Handle函数:
// Handle registers the handler for the given pattern.
// If a handler already exists for pattern, Handle panics.
func (mux *ServeMux) Handle(pattern string, handler Handler) {
mux.mu.Lock()
defer mux.mu.Unlock()
if pattern == "" {
panic("http: invalid pattern")
}
if handler == nil {
panic("http: nil handler")
}
if _, exist := mux.m[pattern]; exist {
panic("http: multiple registrations for " + pattern)
}
if mux.m == nil {
mux.m = make(map[string]muxEntry)
}
mux.m[pattern] = muxEntry{h: handler, pattern: pattern}
if pattern[0] != '/' {
mux.hosts = true
}
}
在map中加入了对应的(patterm, MuxEntry)项,muxEntry结构中存储了存储了对应的pattern和Handler。
至此,实现了路由注册功能。
ListenAndServe():监听并处理request、返回response
http.ListenAndServe(port, nil)
ListenAndServer函数:
func ListenAndServe(addr string, handler Handler) error {
server := &Server{Addr: addr, Handler: handler}
return server.ListenAndServe()
}
创建了一个server对象,并调用它的ListenAndServe函数:
func (srv *Server) ListenAndServe() error {
addr := srv.Addr
if addr == "" {
addr = ":http"
}
ln, err := net.Listen("tcp", addr)
if err != nil {
return err
}
return srv.Serve(tcpKeepAliveListener{ln.(*net.TCPListener)})
}
发起tcp连接的监听,调用server的Serve函数:
// Serve accepts incoming connections on the Listener l, creating a
// new service goroutine for each. The service goroutines read requests and
// then call srv.Handler to reply to them.
//
// HTTP/2 support is only enabled if the Listener returns *tls.Conn
// connections and they were configured with "h2" in the TLS
// Config.NextProtos.
//
// Serve always returns a non-nil error and closes l.
// After Shutdown or Close, the returned error is ErrServerClosed.
func (srv *Server) Serve(l net.Listener) error {
if fn := testHookServerServe; fn != nil {
fn(srv, l) // call hook with unwrapped listener
}
l = &onceCloseListener{Listener: l}
defer l.Close()
if err := srv.setupHTTP2_Serve(); err != nil {
return err
}
if !srv.trackListener(&l, true) {
return ErrServerClosed
}
defer srv.trackListener(&l, false)
var tempDelay time.Duration // how long to sleep on accept failure
baseCtx := context.Background() // base is always background, per Issue 16220
ctx := context.WithValue(baseCtx, ServerContextKey, srv)
for {
rw, e := l.Accept()
if e != nil {
select {
case <-srv.getDoneChan():
return ErrServerClosed
default:
}
if ne, ok := e.(net.Error); ok && ne.Temporary() {
if tempDelay == 0 {
tempDelay = 5 * time.Millisecond
} else {
tempDelay *= 2
}
if max := 1 * time.Second; tempDelay > max {
tempDelay = max
}
srv.logf("http: Accept error: %v; retrying in %v", e, tempDelay)
time.Sleep(tempDelay)
continue
}
return e
}
tempDelay = 0
c := srv.newConn(rw)
c.setState(c.rwc, StateNew) // before Serve can return
go c.serve(ctx)
}
}
}
创建一个上下文对象,然后在for循环中,调用Listener的Accept方法用来获取客户端的每次请求的连接数据,然后使用newConn方法创建一个连接对象,里面保存了该次请求的信息。每个请求都能保持独立,相互不会阻塞,保证了go的高效率。