Go http Server

创建http服务的三种实现方式

方式1

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

func hello(w http.ResponseWriter,r *http.Request)  {
	io.WriteString(w,"hello")
}

方式2

func main() {
	http.Handle("/hello",&ServeMux{})
	err := http.ListenAndServe(":8080",nil)
	if err != nil {
		log.Panic(err)
	}
}

type ServeMux struct {
}

func (p * ServeMux)ServeHTTP(w http.ResponseWriter,r *http.Request)  {
	io.WriteString(w,"hello world")
}

其实方式2就是方式1的一种包装形式,方式1只是将func hello(w http.ResponseWriter,r *http.Request)转化成type HandlerFunc func(ResponseWriter, *Request)类型,而HandlerFunc又实现了Handler接口,所以到这里情况就和方式2一致了

方式1的包装代码:

func HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
	DefaultServeMux.HandleFunc(pattern, handler)
}
func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
	mux.Handle(pattern, HandlerFunc(handler))
}
type HandlerFunc func(ResponseWriter, *Request)
// ServeHTTP calls f(w, r).
func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {
	f(w, r)
}

方式1和方式2都是使用的是系统提供的ServeMux

var DefaultServeMux = &defaultServeMux

var defaultServeMux ServeMux
这里的handler传入的是nil,那么启动服务后是如何找到以上方式相应的Handler接口实现的

err := http.ListenAndServe(":8080",nil)启动服务,该方法的内部实现:

func ListenAndServe(addr string, handler Handler) error {
	server := &Server{Addr: addr, Handler: handler}
	return server.ListenAndServe()
}

使用的tcp连接

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连接后,监听tcp请求,有请求后,创建连接,然后开启一个协程去服务改连接的请求与响应

func (srv *Server) Serve(l net.Listener) error {
	defer l.Close()
	if fn := testHookServerServe; fn != nil {
		fn(srv, l)
	}
	var tempDelay time.Duration // how long to sleep on accept failure

	if err := srv.setupHTTP2_Serve(); err != nil {
		return err
	}

	srv.trackListener(l, true)
	defer srv.trackListener(l, false)

	baseCtx := context.Background() // base is always background, per Issue 16220
	ctx := context.WithValue(baseCtx, ServerContextKey, srv)
	for {
		rw, e := l.Accept()
		//....
		tempDelay = 0
		c := srv.newConn(rw)
		c.setState(c.rwc, StateNew) // before Serve can return
		go c.serve(ctx)
	}
}

响应服务

// Serve a new connection.
func (c *conn) serve(ctx context.Context) {
	c.remoteAddr = c.rwc.RemoteAddr().String()
	//....

	for {
		w, err := c.readRequest(ctx)   //读请求,封装相应请求体和响应体数据
		if c.r.remain != c.server.initialReadLimitSize() {
			// If we read any bytes off the wire, we're active.
			c.setState(c.rwc, StateActive)
		}
		//....

		// Expect 100 Continue support
		req := w.req
		if req.expectsContinue() {
			if req.ProtoAtLeast(1, 1) && req.ContentLength != 0 {
				// Wrap the Body reader with one that replies on the connection
				req.Body = &expectContinueReader{readCloser: req.Body, resp: w}
			}
		} else if req.Header.get("Expect") != "" {
			w.sendExpectationFailed()
			return
		}

		c.curReq.Store(w)

		if requestBodyRemains(req.Body) {
			registerOnHitEOF(req.Body, w.conn.r.startBackgroundRead)
		} else {
			if w.conn.bufr.Buffered() > 0 {
				w.conn.r.closeNotifyFromPipelinedRequest()
			}
			w.conn.r.startBackgroundRead()
		}

		serverHandler{c.server}.ServeHTTP(w, w.req)   //调用Handler处理相应路由的请求函数,
		//....
	}
}

读请求数据

// Read next request from connection.
func (c *conn) readRequest(ctx context.Context) (w *response, err error) {
	//....
	for k, vv := range req.Header {
		if !httplex.ValidHeaderFieldName(k) {
			return nil, badRequestError("invalid header name")
		}
		for _, v := range vv {
			if !httplex.ValidHeaderFieldValue(v) {
				return nil, badRequestError("invalid header value")
			}
		}
	}
	delete(req.Header, "Host")

	ctx, cancelCtx := context.WithCancel(ctx)
	req.ctx = ctx
	req.RemoteAddr = c.remoteAddr
	req.TLS = c.tlsState
	if body, ok := req.Body.(*body); ok {
		body.doEarlyClose = true
	}

	// Adjust the read deadline if necessary.
	if !hdrDeadline.Equal(wholeReqDeadline) {
		c.rwc.SetReadDeadline(wholeReqDeadline)
	}

	w = &response{
		conn:          c,
		cancelCtx:     cancelCtx,
		req:           req,
		reqBody:       req.Body,
		handlerHeader: make(Header),
		contentLength: -1,
		closeNotifyCh: make(chan bool, 1),

		// We populate these ahead of time so we're not
		// reading from req.Header after their Handler starts
		// and maybe mutates it (Issue 14940)
		wants10KeepAlive: req.wantsHttp10KeepAlive(),
		wantsClose:       req.wantsClose(),
	}
	if isH2Upgrade {
		w.closeAfterReply = true
	}
	w.cw.res = w
	w.w = newBufioWriterSize(&w.cw, bufferBeforeChunkingSize)
	return w, nil
}

路由选择(这里就知道为什么http.ListenAndServe(":8080",nil)第二个参数为nil的原因了)

func (sh serverHandler) ServeHTTP(rw ResponseWriter, req *Request) {
	handler := sh.srv.Handler  //获取Server中的Handler
	if handler == nil {   //如果Server中没有Handler,就使用DefaultServeMux,也就是方式1和方式2使用的系统提供

的路由
		handler = DefaultServeMux
	}
	if req.RequestURI == "*" && req.Method == "OPTIONS" {
		handler = globalOptionsHandler{}
	}
	handler.ServeHTTP(rw, req)  //调用路由去执行相应执行函数
}

最后就是通过rw(ResponseWriter)将响应的数据返回给客户端,我简单的跟了下其Write([]byte) (int, error)方法,最后调用的是syscall包的func Syscall6(trap, nargs, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err Errno),不知是否正确。

方式3,(自定义路由,不使用DefaultServeMux)

func main() {
	router := &http.Server{
		Addr:":8080",
		Handler:&RouterMux{},
	}
	err := router.ListenAndServe()
	if err != nil {
		log.Panic(err)
	}
}

type RouterMux struct {
}

func (p * RouterMux)ServeHTTP(w http.ResponseWriter,r *http.Request)  {
	//可以使用map将路径与响应函数管理起来
	//map[string]func(http.ResponseWriter,*http.Request)
	switch r.URL.Path {
	case "/hello":
		io.WriteString(w,"hello router")
	case "/hello2":
		io.WriteString(w,"hello world !!!")
	default:
		io.WriteString(w,r.URL.Path)
	}
}

根据http.ListenAndServe(string,Handler)方法可以看出,方式1和方式2就是在方式3的基础上做了一些简单的封装

func ListenAndServe(addr string, handler Handler) error {
	server := &Server{Addr: addr, Handler: handler}
	return server.ListenAndServe()
}

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值