创建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()
}