zoukankan      html  css  js  c++  java
  • Golang内建库学习笔记(2)-web服务器相关

    package main
    
    import (
        "net/http"
        "fmt"
        "strings"
        "log"
    )
    
    func sayHelloName(w http.ResponseWriter, r *http.Request) {
        r.ParseForm()
        fmt.Println(r.Form)
        fmt.Println("url", r.URL.Path)
        fmt.Println("scheme", r.URL.Scheme)
        fmt.Println(r.Form["url_long"])
        for k, v:=range r.Form {
            fmt.Println("key:", k)
            fmt.Println("value:", strings.Join(v, ""))
        }
        fmt.Fprintln(w, "hello web")
    }
    
    func main()  {
        http.HandleFunc("/", sayHelloName)
        err := http.ListenAndServe(":9876", nil)
        if err!= nil {
            log.Fatal("ListenAndServe ", err)
        }
    }

    先看一个最基本的golang的web服务器代码。main中在HandleFunc中设置了路由,然后调用ListenAndServe选择监听的接口和路由方法(这里路由方法制空,即调用了默认的路由,即HandleFunc设置的路由)。

    LestenAndServe调用了http库中的serve方法,代码如下:

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

    可以看到,serve建立了一个循环来监听请求。在接收到连接请求后,先接受请求,然后新建了连接,并利用go新建了线程来处理新的连接。这就是golang在处理web请求上支持高并发的根本。同时也保证了每个连接的独立性。

    路由设置:

    func (sh serverHandler) ServeHTTP(rw ResponseWriter, req *Request) {
        handler := sh.srv.Handler
        if handler == nil {
            handler = DefaultServeMux
        }
        if req.RequestURI == "*" && req.Method == "OPTIONS" {
            handler = globalOptionsHandler{}
        }
        handler.ServeHTTP(rw, req)
    }

    ServeHTTP方法负责设置路由。可以看到,如果handler为空,就使用默认路由。否则就使用自定义路由。这个方法是Handler接口中规定的唯一方法。对于我们的例子,sayHelloName方法并没有实现这个方法,仍然实现了Handler接口。这是因为HandleFunc方法将路由实现方法强制转换为HandleFunc类型。该方法实现了Handler接口。

    handler处理方法如下:

    func (mux *ServeMux) handler(host, path string) (h Handler, pattern string) {
        mux.mu.RLock()
        defer mux.mu.RUnlock()
    
        // Host-specific pattern takes precedence over generic ones
        if mux.hosts {
            h, pattern = mux.match(host + path)
        }
        if h == nil {
            h, pattern = mux.match(path)
        }
        if h == nil {
            h, pattern = NotFoundHandler(), ""
        }
        return
    }

    可以看到其匹配路由的方式。

    根据以上的内容,我们也可以自己自定义路由。自定义路只需实现Handler接口。

    type MyMux struct {
    }
    
    func (m *MyMux) ServeHTTP(w http.ResponseWriter, r *http.Request)  {
        if r.URL.Path == "/" {
            sayHelloName(w, r)
            return
        }
        http.NotFound(w, r)
        return
    }
    
    func main()  {
        mux := &MyMux{}
        http.ListenAndServe(":9876", mux)
    }
  • 相关阅读:
    go语言基础学习
    VBA汇总同目录下的所有工作簿数据到另一个工作簿,并进行统计
    彻底关闭win10后台同步数据(转自技术社区)
    在WIN10上安装ESXI-Comstomer (转自技术社区)
    squid代理允许FTP访问设置
    Powershell 脚本判断制定路径下文件是否存在(来源于网络-转载)
    Python集合(set)类型的操作 (转)
    python3.5.2中文字符乱码问题解决
    Debian 中文环境设置
    Python 列表推导实例
  • 原文地址:https://www.cnblogs.com/wangzhao765/p/9037614.html
Copyright © 2011-2022 走看看