zoukankan      html  css  js  c++  java
  • go 搭建web服务

    Go语言标准库 - net/http

    在学习Go语言有一个很好的起点,Go语言官方文档很详细,今天我们学习的Go Web服务器的搭建就需要用到Go语言官方提供的标准库 net/http,通过http包提供了HTTP客户端和服务端的实现。同时使用这个包能很简单地对web的路由,静态文件,模版,cookie等数据进行设置和操作。

    下面是用http包建立web服务器的例子:

    1、使用http.HandleFunc

    package main
    import (
        "fmt"
        "net/http"
        "strings"
        "log"
    )
    func doRequest(w http.ResponseWriter, r *http.Request) {
        r.ParseForm() //解析参数,默认是不会解析的
        fmt.Println(r.Form) //这些信息是输出到服务器端的打印信息
        fmt.Println("path", 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("val:", strings.Join(v, ""))
        }
        fmt.Fprintf(w, "Hello Wrold!") //这个写入到w的是输出到客户端的
    }
    func main() {
        http.HandleFunc("/", doRequest) //设置访问的路由
        err := http.ListenAndServe(":9090", nil) //设置监听的端口
        if err != nil {
            log.Fatal("ListenAndServe: ", err)
        }
    }

    上面的代码我们在IDE中编译后并运行成功后,这个时侯我们就可以在9090端口监听http链接请求了。

     我们看到了上面的代码,要编写一个Web服务器是不是很简单,只要调用http包的两个函数就可以了。

     在main函数中,我们从net/http包中调用了一个http.HandleFucn函数来注册一个处理函数,这个函数接受两个参数。第一个是字符串,这个就是进行路由匹配,我这里是/路由。第二个参数是一个func (ResponseWriter, Request)的签名。

    我们的doRequest函数就是这样的签名。下一行中的http.ListenAndServe(":8000", nil),表示监听localhost的9090端口,暂时忽略掉nil。

    在doRequest函数中我们有两个参数,一个是http.ResponseWriter类型的。它类似响应流,实际上是一个接口类型。

    第二个是http.Request类型,类似于HTTP 请求。我们不必使用所有的参数,如果只是简单的输出,那么我们只需要使用http.ResponseWriter,io.WriteString,将会把输出流写入数据。

    我们再稍微改下,大家请注意修改的部分(这里我们只调整 main函数部分代码)

    2、使用ServeMux

    func main() {
        mux := http.NewServeMux()
        mux.HandleFunc("/", doRequest)
        err :=  http.ListenAndServe(":9090", mux) //设置监听的端口
        if err != nil {
            log.Fatal("ListenAndServe: ", err)
        }
    }

    这个例子中,我们不再在函数http.ListenAndServe使用nil了。这个例子跟上面的例子其实是一样的。使用http注册hanlder 函数模式就是用的ServeMux。

    3、使用http.Server,而不是ServeMux。它们都是用net/http包运行了服务器。

    package main
    
    import (
        "fmt"
        "io"
        "log"
        "net/http"
    )
    
    var mux map[string]func(http.ResponseWriter, *http.Request)
    
    func main() {
        server := http.Server{
            Addr:    ":8000",
            Handler: &doHandler{},
        }
    
        mux = make(map[string]func(http.ResponseWriter, *http.Request))
        mux["/test"] = doRequest
    
        err := server.ListenAndServe()
        if err != nil {
            log.Fatal("ListenAndServe: ", err)
        }
    }
    
    type doHandler struct{}
    
    func (*doHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
        if res, ok := mux[r.URL.String()]; ok {
            res(w, r)
            return
        }
    
        io.WriteString(w, "url params: "+r.URL.String())
    }
    func doRequest(w http.ResponseWriter, r *http.Request) {
        r.ParseForm()                      //解析url传递的参数,对于POST则解析响应包的主体(request body)
        fmt.Fprintf(w, "service start...") //这个写入到w的是输出到客户端的 也可以用下面的 io.WriteString对象
    }

    如果以前你是.NET程序员或java,那你也许就会问,我们的IIS服务器,tomcat服务器不需要吗?Go就是不需要这些,因为他直接就监听了TCP端口了。

    二、Web工作方式的几个概念

    我们看到Go通过简单的几行代码就已经运行起来一个Web服务了,而且这个Web服务内部有支持高并发的特性。现在Web服务已经搭建完成了,那我们现在来了解一个这个服务是怎么运行起来的呢?

    以下几个为服务器段的概念

    • Request:用户请求的信息,用来解析用户的请求信息,包括post、get、cookie、url等信息
    • Response:服务器需要反馈给客户端的信息
    • Conn:用户的每次请求链接
    • Handler:处理请求和生成返回信息的处理逻辑

    分析http包运行机制

     Go实现Web服务的工作模式流程图

    这个过程我们需要清楚以下三个问题,则就清楚Go是如何让Web运行起来了

      • 如何监听端口?
        通过上面的代码我们看到Go是通过一个函数ListenAndServe来处理这些事情的,这个底层其实这样处理的:初始化一个server对象,然后调用了net.Listen("tcp", addr),也就是底层用TCP协议搭建了一个服务,然后监控我们设置的端口。

          Go http包的源码,这里我们可以看到整个http处理过程

    func (srv *Server) Serve(l net.Listener) error {
        defer l.Close()
        var tempDelay time.Duration // how long to sleep on accept failure
        for {
            rw, e := l.Accept()
            if e != nil {
                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
                    }
                    log.Printf("http: Accept error: %v; retrying in %v", e, tempDelay)
                    time.Sleep(tempDelay)
                    continue
                }
                return e
            }
            tempDelay = 0
            if srv.ReadTimeout != 0 {
                rw.SetReadDeadline(time.Now().Add(srv.ReadTimeout))
            }
            if srv.WriteTimeout != 0 {
                rw.SetWriteDeadline(time.Now().Add(srv.WriteTimeout))
            }
            c, err := srv.newConn(rw)
            if err != nil {
                continue
            }
            go c.serve()
        }
        panic("not reached")
    }
      • 如何接收客户端请求?
        上面代码执行监控端口之后,调用了srv.Serve(net.Listener)函数,这个函数就是处理接收客户端的请求信 息。这个函数里面起了一个for{},首先通过Listener接收请求,其次创建一个 Conn,最后单独开了一个 goroutine,把这个请求的数据当做参数扔给这个conn去服务:go c.serve()。这 个就是高并发体现了, 用户的每一次请求都是在一个新的goroutine去服务,相互不影响。
      • 如何分配handler?
        conn首先会解析request:c.readRequest(),然后获取相应的handler:handler := c.server.Handler,也就是我们刚才在调用函数ListenAndServe时候的第二个参数,我们前面例子传递的是nil,也就是为空,那么默认获取handler = DefaultServeMux,那么这个变量用来做什么的呢?对,这个变量就是一个路由器,它用来匹配url跳转到其相应的handle函数,那么这个我们有设置过吗?有,我们调用的代码里面第一句不是调用了http.HandleFunc("/", sayhelloName)嘛。这个作用就是注册了请求/的路由规则,当请求uri为"/",路由就会转到函数sayhelloName,DefaultServeMux会调用ServeHTTP方法,这个方法内部其实就是调用sayhelloName本身,最后通过写入response的信息反馈到客户端。

    一个http连接处理流程

    至此我们的三个问题已经全部得到了解答,你现在对于Go如何让Web跑起来的是否已经基本了解。

    转:https://www.cnblogs.com/franklee97/p/7131551.html

  • 相关阅读:
    LeetCode 81 Search in Rotated Sorted Array II(循环有序数组中的查找问题)
    LeetCode 80 Remove Duplicates from Sorted Array II(移除数组中出现两次以上的元素)
    LeetCode 79 Word Search(单词查找)
    LeetCode 78 Subsets (所有子集)
    LeetCode 77 Combinations(排列组合)
    LeetCode 50 Pow(x, n) (实现幂运算)
    LeetCode 49 Group Anagrams(字符串分组)
    LeetCode 48 Rotate Image(2D图像旋转问题)
    LeetCode 47 Permutations II(全排列)
    LeetCode 46 Permutations(全排列问题)
  • 原文地址:https://www.cnblogs.com/duanxz/p/12589142.html
Copyright © 2011-2022 走看看