Go Web编程一: Go Web 基础
来源:互联网 发布:for循环鸡兔同笼编程 编辑:程序博客网 时间:2024/06/05 15:36
原文链接 http://ironxu.com/779
Go Web 基础概念与代码阅读
1. Go 搭建简单的web 服务
Go 语言里面提供了一个完善的 net/http
包,通过http
包可以很方便的就搭建起来一个可以运行的Web服务。同时使用这个包能很简单地对Web的路由,静态文件,模版,cookie等进行设置和操作。
$GOPATH/src/github.com/ironxu/go_note/web/basic/server.go
源码如下:
// http 包建立web 服务器package mainimport ( "fmt" "log" "net/http")func sayhelloName(w http.ResponseWriter, r *http.Request) { r.ParseForm() fmt.Println("path:", r.URL.Path) fmt.Fprintf(w, "hello go")}func main() { http.HandleFunc("/", sayhelloName) err := http.ListenAndServe(":9090", nil) if err != nil { log.Fatal("ListenAndServer: ", err) }}
go run server.go
即可启动http 服务,使用浏览器打开 http://localhost:9090 可以查看相应输出。
2. Go Web 服务讲解
本节介绍 Go Web 服务底层实现,包括注册路由和请求处理
2.1 HTTP 包运行机制
Go 实现Web 服务流程如下
- 创建
Listen Socket
, 监听指定的端口, 等待客户端请求到来。 Listen Socket
接受客户端的请求, 得到Client Socket
, 接下来通过Client Socket
与客户端通信。- 处理客户端的请求, 首先从
Client Socket
读取HTTP请求, 然后交给相应的handler
处理请求, 最后将handler
处理完毕的数据, 通过Client Socket
写给客户端。
其中涉及服务器端的概念:
- Request:用户请求的信息,用来解析用户的请求信息,包括post、get、cookie、url等信息
- Conn:用户的每次请求链接
- Handler:处理请求和生成返回信息的处理逻辑
- Response:服务器需要反馈给客户端的信息
2.2 服务监听与请求处理过程
Go是通过一个ListenAndServe
监听服务,底层处理:初始化一个server
对象,然后调用 net.Listen("tcp", addr)
,监控我们设置的端口。
监控端口之后,调用 srv.Serve(net.Listener)
函数,处理接收客户端的请求信息。首先通过Listener
接收请求,其次创建一个Conn,最后单独开了一个goroutine
,把这个请求的数据当做参数扔给这个conn
去服务。go c.serve()
用户的每一次请求都是在一个新的goroutine去服务,相互不影响。
分配相应的函数处理请求: conn
首先会解析 request:c.readRequest()
, 然后获取相应的handler:handler := c.server.Handler
,这个是调用函数ListenAndServe
时候的第二个参数,例子传递的是nil,也就是为空,那么默认获取handler = DefaultServeMux
。DefaultServeMux
是一个路由器,它用来匹配url跳转到其相应的handle函数
调用 http.HandleFunc("/", sayhelloName)
作用是注册了请求/的路由规则,将url 和handle 函数注册到DefaultServeMux
变量,最后调用DefaultServeMux
的ServeHTTP
方法,这个方法内部调用handle 函数。
流程图如下:
3. Web 服务代码实现
3.1 路由注册代码
1 调用 http.HandleFunc(“/”, sayhelloName) 注册路由
// /usr/local/go/src/net/http/server.go:2081func HandleFunc(pattern string, handler func(ResponseWriter, *Request)) { DefaultServeMux.HandleFunc(pattern, handler) // DefaultServeMux 类型为 *ServeMux}
2 使用默认 ServeMux
// :2027func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Request)) { mux.Handle(pattern, HandlerFunc(handler))}
3 注册路由策略 DefaultServeMux
func (mux *ServeMux) Handle(pattern string, handler Handler) { mux.mu.Lock() defer mux.mu.Unlock() ... mux.m[pattern] = muxEntry{explicit: true, h: handler, pattern: pattern} if pattern[0] != '/' { mux.hosts = true } ...}
涉及数据结构
// :1900 ServeMux 默认实例是 DefaultServeMuxtype ServeMux struct { mu sync.RWMutex // 锁,由于请求涉及到并发处理,因此这里需要一个锁机制 m map[string]muxEntry // 路由规则,一个string对应一个mux实体,这里的string就是注册的路由表达式 hosts bool // 是否在任意的规则中带有host信息}type muxEntry struct { explicit bool h Handler // 路由处理器 pattern string // url 匹配正则}type Handler interface { ServeHTTP(ResponseWriter, *Request)}
3.2 服务监听代码
1 调用 err := http.ListenAndServe(“:9090”, nil) 监听端口
// /usr/local/go/src/net/http/server.go:2349func ListenAndServe(addr string, handler Handler) error { server := &Server{Addr: addr, Handler: handler} // handler 为空 return server.ListenAndServe()}
创建一个 Server 对象,并调用 Server 的 ListenAndServe()
2 监听TCP端口
// :2210func (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)})}
3 接收请求
// :2256func (srv *Server) Serve(l net.Listener) error { defer l.Close() ... baseCtx := context.Background() ctx := context.WithValue(baseCtx, ServerContextKey, srv) ctx = context.WithValue(ctx, LocalAddrContextKey, l.Addr()) for { rw, e := l.Accept() // 1. Listener 接收请求 if e != nil { ... } tempDelay = 0 c := srv.newConn(rw) // 2. 创建 *conn c.setState(c.rwc, StateNew) // before Serve can return go c.serve(ctx) // 3. 新启一个goroutine,将请求数据做为参数传给 conn,由这个新的goroutine 来处理这次请求 }}
4 goroutine 处理请求
// Serve a new connection.func (c *conn) serve(ctx context.Context) { ... // HTTP/1.x from here on. c.r = &connReader{r: c.rwc} c.bufr = newBufioReader(c.r) c.bufw = newBufioWriterSize(checkConnErrorWriter{c}, 4<<10) ctx, cancelCtx := context.WithCancel(ctx) defer cancelCtx() for { w, err := c.readRequest(ctx) // 1. 获取请求数据 ... serverHandler{c.server}.ServeHTTP(w, w.req) // 2. 处理请求 serverHandler, 对应下面第5步 w.cancelCtx() if c.hijacked() { return } w.finishRequest() // 3. 返回响应结果 if !w.shouldReuseConnection() { if w.requestBodyLimitHit || w.closedRequestBodyEarly() { c.closeWriteAndWait() } return } c.setState(c.rwc, StateIdle) }}
5 处理请求
func (sh serverHandler) ServeHTTP(rw ResponseWriter, req *Request) { handler := sh.srv.Handler if handler == nil { handler = DefaultServeMux // ServeMux } if req.RequestURI == "*" && req.Method == "OPTIONS" { handler = globalOptionsHandler{} } handler.ServeHTTP(rw, req)}
5.1 handler.ServeHTTP(rw, req)
func (mux *ServeMux) ServeHTTP(w ResponseWriter, r *Request) { if r.RequestURI == "*" { if r.ProtoAtLeast(1, 1) { w.Header().Set("Connection", "close") } w.WriteHeader(StatusBadRequest) return } h, _ := mux.Handler(r) // HandlerFunc, Handler h.ServeHTTP(w, r)}
5.2 执行处理
// ServeHTTP calls f(w, r).func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) { f(w, r)}
涉及的数据类型
type Server struct { Addr string // TCP address to listen on, ":http" if empty Handler Handler // handler to invoke, http.DefaultServeMux if nil ReadTimeout time.Duration // maximum duration before timing out read of the request WriteTimeout time.Duration // maximum duration before timing out write of the response ...}type conn struct { server *Server // server is the server on which the connection arrived. rwc net.Conn // rwc is the underlying network connection. It is usually of type *net.TCPConn or *tls.Conn. remoteAddr string // This is the value of a Handler's (*Request).RemoteAddr. mu sync.Mutex // mu guards hijackedv, use of bufr, (*response).closeNotifyCh. ...}type serverHandler struct { srv *Server}
3.3 Go 代码的执行流程
调用Http.HandleFunc
,按顺序做了几件事:
- 调用了DefaultServeMux的HandleFunc
- 调用了DefaultServeMux的Handle
- 往DefaultServeMux的map[string]muxEntry中增加对应的handler和路由规则
调用http.ListenAndServe(":9090", nil)
,按顺序做了几件事情:
- 实例化Server
- 调用Server的ListenAndServe()
- 调用net.Listen(“tcp”, addr)监听端口
- 启动一个for循环,在循环体中Accept请求
- 对每个请求实例化一个Conn,并且开启一个goroutine为这个请求进行服务go c.serve()
- 读取每个请求的内容w, err := c.readRequest()
- 判断handler是否为空,如果没有设置handler(这个例子就没有设置handler),handler就设置为DefaultServeMux
- 调用handler的ServeHttp
- 在这个例子中,下面就进入到DefaultServeMux.ServeHttp
- 根据request选择handler,并且进入到这个handler的ServeHTTP mux.handler(r).ServeHTTP(w, r)
- 选择handler:
A 判断是否有路由能满足这个request(循环遍历ServerMux的muxEntry)
B 如果有路由满足,调用这个路由handler的ServeHttp
C 如果没有路由满足,调用NotFoundHandler的ServeHttp
4. 自定义路由实现
定义的类型实现ServeHTTP
方法,即可实现自定义路由
package mainimport ( "fmt" "log" "net/http")type MyMux struct {}func (p *MyMux) ServeHTTP(w http.ResponseWriter, r *http.Request) { if r.URL.Path == "/" { sayhelloName(w, r) return } http.NotFound(w, r) return}func sayhelloName(w http.ResponseWriter, r *http.Request) { r.ParseForm() fmt.Println("path:", r.URL.Path) fmt.Fprintf(w, "hello go")}func main() { mux := &MyMux{} err := http.ListenAndServe(":9090", mux) if err != nil { log.Fatal("ListenAndServer: ", err) }}
参考
- 3.2 Go搭建一个Web服务器
- 3.3 Go如何使得Web工作
- 3.4 Go的http包详解
可以关注我的微博了解更多信息: @刚刚小码农
- Go Web编程一: Go Web 基础
- Go Web编程:Web基础
- Go Web开发基础
- Go web编程学习(一)
- Go Web编程
- go web 编程
- go的web编程
- Go Web基础--Beego框架
- go后台和web编程
- Go Web编程:访问数据库
- 理解Go语言Web编程
- 《Go web编程》ChitChat论坛
- Go Web 开发(一)
- 采访:关于 Go 语言和《Go Web编程》
- 采访:关于Go语言和《Go Web编程》
- Go Web基础--beego ORM使用
- Go Web编程:http包分析
- go web编程-原生库实现
- 写给软件学院的本科生(转)
- Vim可选插件
- MyBatis之select
- unity 的内存优化工具 Memory Profiler
- 发现一个c语言中用字符数组和char型指针管理字符串一个很容易搞混的地方
- Go Web编程一: Go Web 基础
- Fodera使用教程(不断更新中)
- 分享tcp与udp的异同
- OI竞赛中手工栈的书写
- 关于cctype头文件
- 廖雪峰老师——Python入门( List和Tuple类型 )
- 数据库基础知识
- PHP+MySQL环境搭建简说
- 《hive编程指南》阅读笔记摘要(六)