Go学习(二十):启动HTTP服务的方式

1. 怎么启动Web服务?

Go语言标准库内置的net/http包,可以实现HTTP服务端。实现HTTP服务端就是能够启动Web服务,相当于搭建起了一个Web服务器。 http.ListenAndServer()函数用来启动Web服务,绑定并监听http端口。

func ListenAndServe(addr string, handler Handler)
// addr:监听地址;如 :8080 或者0.0.0.0:8080
// handler:HTTP处理器Handler

2.启动Web服务的几种方式

根据不同服务返回的handler,常见启动Web服务有以下几种方式。

2.1 http.FileServer: 静态文件服务

http.FileServer()搭建的服务器只提供静态文件的访问。因为这种web服务只支持静态文件访问,所以称之为静态文件服务。

1.使用示例

文件目录如下:

image-20201228174249803
package main
import (
 "net/http"
)
func main() {
 runFileServer()
}
// 启动一个文件服务器
func runFileServer()  {
 // 如果改路径下有index.html,则会优先显示index.html,否则会看到文件目录
 http.ListenAndServe(":3000",http.FileServer(http.Dir("./public/")))
}

注意: 启动的时候,需求使用go run main.go。否则会报404

2.2 http.HandleFunc: 默认的多路由分发服务

http.HandleFunc()的作用是注册网络访问的路由。因为它采用的是默认的路由分发任务方式,所以称之为默认的多路由分发服务。

func HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
 DefaultServeMux.HandleFunc(pattern, handler)
}
// pattern:请求路径的匹配模式
// handler:函数类型,表示这个请求需要处理的事情,其实就是Handler接口中的ServeHTTP()方法。

ServeHTTP()方法有两个参数,其中第一个参数是ResponseWriter类型,包含了服务器端给客户端的响应数据。服务器端往ResponseWriter写入了什么内容,浏览器的网页源码就是什么内容。第二个参数是一个 *Request指针,包含了客户端发送给服务器端的请求信息(路径、浏览器类型等)

1.使用示例
package main
import (
 "fmt"
 "net/http"
)
func main() {
 // 绑定路由/hello
 http.HandleFunc("/hello",helloHandle)
 // 绑定路由到/test
 http.HandleFunc("/test",testHandle)
 // 启动服务
 err := http.ListenAndServe(":5000",nil)
 fmt.Println(err)
}
// 处理路由hello
func helloHandle(w http.ResponseWriter, r *http.Request)  {
 fmt.Println("访问路由hello")
 // 解析url参数
 fmt.Println(r.URL.Query())
 w.Write([]byte("hello word!"))
}
// 处理路由test
func testHandle(w http.ResponseWriter, r *http.Request)  {
 fmt.Println("访问路由test")
 // 解析url参数,并输出
 fmt.Println(r.URL.Query())
 w.Write([]byte("test doing!"))
}
// 访问: http://127.0.0.1:5000/test?a=1001
// 访问: http://127.0.0.1:5000/hello?b=990

通过http. HandleFunc()注册网络路由时,http.ListenAndServer()的第二个参数通常为nil,这意味着服务端采用默认的http.DefaultServeMux进行分发处理。

2.3 http.NewServeMux(): 自定义多路由分发服务

http.NewServeMux()的作用是注册网络访问的多路路由。因为它采用的是自定义的多路由分发任务方式,所以称之为自定义多路由分发服务。

注册网络路由时,如果http.ListenAndServer()的第二个参数为nil,那么表示服务端采用默认的http.DefaultServeMux进行分发处理。也可以自定义ServeMuxServeMux结构体如下

// ServeMux结构体源码
type ServeMux struct {
 mu    sync.RWMutex // 锁,由于请求涉及到并发处理,因此这里有个锁机制 
 m     map[string]muxEntry // 存放具体的路由信息 
 es    []muxEntry // 按照路由长度从大到小的存放处理函数
 hosts bool       // 标记路由中是否带有主机名
}
// muxEntry是路由的具体条目
type muxEntry struct {
 h       Handler // 处理函数
 pattern string  // 路由路径
}

3. 自定义多路由实践

package main
import (
 "fmt"
 "net/http"
)
// 定义一个接口体,用来实现http.Handler
type MyRoute struct {
}
// 实现http.Handler接口中的ServeHTTP方法
func (m *MyRoute)ServeHTTP(w http.ResponseWriter,r *http.Request)  {
 path := r.URL.Path
 fmt.Println(path)
 switch path {
 case  "/":
  w.Write([]byte("首页"))
 case "/hello":
  w.Write([]byte("say hello"))
 case "/test":
  w.Write([]byte("test doing"))
 default:
  http.NotFound(w,r)
 }
 return
}
func main() {
 myRoute := &MyRoute{}
 http.ListenAndServe(":10000",myRoute)
}
3.1 代码执行流程

使用http.ListenAndServe(":10000",myRoute)启动服务之后,会发生以下操作

1.实例化http.Server,并调用ListenAndServe()
func ListenAndServe(addr string, handler Handler) error {
  // 实例化 Server
 server := &Server{Addr: addr, Handler: handler}
  // 调用 ListenAndServe()
 return server.ListenAndServe()
}
2.监听端口
   func (srv *Server) ListenAndServe() error {
    if srv.shuttingDown() {
     return ErrServerClosed
    }
    addr := srv.Addr
    if addr == "" {
     addr = ":http"
    }
     // 监听端口
    ln, err := net.Listen("tcp", addr)
    if err != nil {
     return err
    }
     // 启动服务
    return srv.Serve(ln)
   }
3.启动for无限循环,在循环体中Accept请求,并开启 goroutine为这个请求服务
func (srv *Server) Serve(l net.Listener) error {
   //...省略N行代码
 for {
  rw, err := l.Accept()
  if err != nil {
    //...省略N行代码
  }
  connCtx := ctx
  if cc := srv.ConnContext; cc != nil {
   connCtx = cc(connCtx, rw)
   if connCtx == nil {
    panic("ConnContext returned nil")
   }
  }
  tempDelay = 0
  c := srv.newConn(rw)
  c.setState(c.rwc, StateNew)
    // 开启goroutine为这个请求服务
  go c.serve(connCtx)
 }
}
4.读取每个请求内容,并调用ServeHTTP
func (c *conn) serve(ctx context.Context) {
 //...省略N行代码
 for {
    // 读取每个请求内容
  w, err := c.readRequest(ctx)
    //...省略N行代码
    
    // 调用ServeHTTP
  serverHandler{c.server}.ServeHTTP(w, w.req)
  //...省略N行代码

5. 判断handler是否为空,如果为空则把handler设置成DefaultServeMux
func (sh serverHandler) ServeHTTP(rw ResponseWriter, req *Request) {
 handler := sh.srv.Handler
 if handler == nil {
    // 如果为空则把handler设置成DefaultServeMux。
  handler = DefaultServeMux
 }
 if req.RequestURI == "*" && req.Method == "OPTIONS" {
  handler = globalOptionsHandler{}
 }
  // 上述示例中,传的是&MyRoute,所以会调用MyRoute.ServeHTTP
 handler.ServeHTTP(rw, req)
}

微信搜索关注【猿码记】查看更多文章。

本文由 mdnice 多平台发布

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值