创建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 = &defaultServeMuxvar 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.Addrif 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 failureif 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 16220ctx := context.WithValue(baseCtx, ServerContextKey, srv)for {rw, e := l.Accept()//....tempDelay = 0c := srv.newConn(rw)c.setState(c.rwc, StateNew) // before Serve can returngo 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 supportreq := w.reqif req.expectsContinue() {if req.ProtoAtLeast(1, 1) && req.ContentLength != 0 {// Wrap the Body reader with one that replies on the connectionreq.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 = ctxreq.RemoteAddr = c.remoteAddrreq.TLS = c.tlsStateif 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 = ww.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中的Handlerif 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()
}

Go http Server相关推荐

  1. 【Sql Server】数据库的3大服务

    在数据库SQL SERVER中,处理常用的sql server数据库引擎,还有其他3大服务,分别是集成服务,报表服务,分析服务. 集成服务商可以配置包,这里的包可以理解是数据库引擎里的用户数据库.可以 ...

  2. 【Sql Server】DateBase-自动化

    强大的SQL Server有着神奇的自动化功能,来处理一些人为处理不了的事情! 自动化功能:T-sql语言,系统命令.脚本语言.复制命令.创建角色.索引重建.报表 管理元素:作业.警报.操作员.计划 ...

  3. 【Sql Server】DateBase-触发器

    触发器是数据库中一种特殊的对象,当使用触发器时,一旦执行某个操作,就会触发执行的一段代码! 语法: Create trigger 名 On[table|view] { For after instea ...

  4. 【Sql Server】DateBase-视频总结

    最近看完了数据库视频,内容繁杂,但也不是无迹可寻! 这是第三遍关于数据库的学习了,随着一遍一遍的深入,更加了解了它的全貌,虽然现在对于数据库还不能到达熟练操作的地步,但至少放我手上不在犯怵了. SQL ...

  5. sql server登录名、服务器角色、数据库用户、数据库角色、架构区别联系

    原创链接:https://www.cnblogs.com/lxf1117/p/6762315.html sql server登录名.服务器角色.数据库用户.数据库角色.架构区别联系 1.一个数据库用户 ...

  6. 分布式TensorFlow集群local server使用详解

    通过local server理解分布式TensorFlow集群的应用与实现. ​​简介 TensorFlow从0.8版本开始,支持分布式集群,并且自带了local server方便测试. Local ...

  7. 合肥工业大学—SQL Server数据库实验十:用户及其权限管理

    用户及其权限管理 1. 创建登录名Mylog及密码 2. 创建用户user2关联登录名 3. 创建角色role1 4. 对用户user2及角色role1授权 5. 验证用户授权 6. 收回用户权限 1 ...

  8. 合肥工业大学—SQL Server数据库实验四:数据库的分离和附加

    数据库的分离和附加 1. 数据库分离 2. 数据库附加 1. 数据库分离 当SQL Server服务器运行时,该服务器上所有的数据库自动处于运行状态,而运行中的数据库文件是无法进行数据库文件的拷贝的. ...

  9. 用户自定义协议client/server代码示例

    用户自定义协议client/server代码示例 代码参考链接:https://github.com/sogou/workflow message.h message.cc server.cc cli ...

  10. mysql navicat导入bcp_SQL Server中BCP导入导出用法详解

    bcp命令是SQL Server提供的一个快捷的数据导入导出工具.使用它不需要启动任何图形管理工具就能以高效的方式导入导出数据.bcp是SQL Server中负责导入导出数据的一个命令行工具,它是基于 ...

最新文章

  1. 欢迎参加“城市大脑与应急管理”专家研讨会
  2. 目标还是中国人,纽约智慧城市项目想通过EB-5募资10亿
  3. c# 找出目录下的所有子目录_Linux操作系统文件目录
  4. 国开大学计算机应用基础作业二,国家开放大学《计算机应用基础》形考作业二答案解析 (2)...
  5. linux系统初级管理书,Linux系统管理基础--超级适合Linux新手的书
  6. Linux pwd命令:显示当前路径
  7. 百变方块java代码_多牛百变方块
  8. Ubuntu18.04安装“迅雷“
  9. JavaWeb:JavaScripts高级
  10. 单位圆盘的全纯自同构群Aut B(0,1)
  11. ubantu下安装redis
  12. 简易2D横版RPG游戏制作
  13. stream_kws_cnn
  14. 购物小票的巧妙新设计
  15. 用字体,每次都要先开软件,ifonts闪退?不好用怎么办
  16. 字符串_字符串的复制
  17. 新手学习【菜鸟教程】Python CGI编程的几个坑(Windows系统)
  18. 从小白到web渗透工程师——零基础指南(1)web渗透工程师介绍
  19. DBA平均月薪17000,入职3年感叹这份工作实在是太难了!
  20. BUUCTF-Crypto学习笔记(二)

热门文章

  1. matlab三角二倍角公式,数学三角函数常用的二倍角公式
  2. 什么是SQL Server数据字典?
  3. PHP中单引号与双引号用法
  4. 《东周列国志》第七十六回 楚昭王弃郢西奔 伍子胥掘墓鞭尸
  5. win10系统如何配置web服务器,如何在windows10下搭建web服务器
  6. office 论文 页码_Word中的论文页码怎么设置?
  7. Zetero+zotfile+坚果云配置
  8. 如何通过组织管理提升团队战斗力?每个CTO都需要了解的三大支柱
  9. html用户名和用户密码验证,js实现用户名和密码的校验
  10. mybatis报错:Could not find resource mapper/UserMapper.xml