Browse Tag: go

Go Web编程一: Go Web 基础

Go Web 基础概念与代码阅读

1. Go 搭建简单的web 服务

Go 语言里面提供了一个完善的 net/http 包,通过http 包可以很方便的就搭建起来一个可以运行的Web服务。同时使用这个包能很简单地对Web的路由,静态文件,模版,cookie等进行设置和操作。

$GOPATH/src/github.com/ironxu/go_note/web/basic/server.go 源码如下:

go run server.go 即可启动http 服务,使用浏览器打开 http://localhost:9090 可以查看相应输出。

2. Go Web 服务讲解

本节介绍 Go Web 服务底层实现,包括注册路由和请求处理

2.1 HTTP 包运行机制

Go 实现Web 服务流程如下

  1. 创建Listen Socket, 监听指定的端口, 等待客户端请求到来。
  2. Listen Socket 接受客户端的请求, 得到Client Socket, 接下来通过Client Socket与客户端通信。
  3. 处理客户端的请求, 首先从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 = DefaultServeMuxDefaultServeMux 是一个路由器,它用来匹配url跳转到其相应的handle函数

调用 http.HandleFunc("/", sayhelloName) 作用是注册了请求/的路由规则,将url 和handle 函数注册到DefaultServeMux 变量,最后调用DefaultServeMuxServeHTTP 方法,这个方法内部调用handle 函数。

流程图如下:

3. Web 服务代码实现

3.1 路由注册代码

1 调用 http.HandleFunc(“/”, sayhelloName) 注册路由

2 使用默认 ServeMux

3 注册路由策略 DefaultServeMux

涉及数据结构

3.2 服务监听代码

1 调用 err := http.ListenAndServe(“:9090”, nil) 监听端口

创建一个 Server 对象,并调用 Server 的 ListenAndServe()

2 监听TCP端口

3 接收请求

4 goroutine 处理请求

5 处理请求

5.1 handler.ServeHTTP(rw, req)

5.2 执行处理

涉及的数据类型

3.3 Go 代码的执行流程

调用Http.HandleFunc,按顺序做了几件事:

  1. 调用了DefaultServeMux的HandleFunc
  2. 调用了DefaultServeMux的Handle
  3. 往DefaultServeMux的map[string]muxEntry中增加对应的handler和路由规则

调用http.ListenAndServe(":9090", nil),按顺序做了几件事情:

  1. 实例化Server
  2. 调用Server的ListenAndServe()
  3. 调用net.Listen(“tcp”, addr)监听端口
  4. 启动一个for循环,在循环体中Accept请求
  5. 对每个请求实例化一个Conn,并且开启一个goroutine为这个请求进行服务go c.serve()
  6. 读取每个请求的内容w, err := c.readRequest()
  7. 判断handler是否为空,如果没有设置handler(这个例子就没有设置handler),handler就设置为DefaultServeMux
  8. 调用handler的ServeHttp
  9. 在这个例子中,下面就进入到DefaultServeMux.ServeHttp
  10. 根据request选择handler,并且进入到这个handler的ServeHTTP mux.handler(r).ServeHTTP(w, r)
  11. 选择handler:
    A 判断是否有路由能满足这个request(循环遍历ServerMux的muxEntry)
    B 如果有路由满足,调用这个路由handler的ServeHttp
    C 如果没有路由满足,调用NotFoundHandler的ServeHttp

4. 自定义路由实现

定义的类型实现ServeHTTP 方法,即可实现自定义路由

参考

可以关注我的微博了解更多信息: @刚刚小码农

Go 标准库介绍六: log

本文介绍Go 标准库 log 的常用导出函数,结构体及方法。

log 包实现了简单的日志服务,log 包定义了Logger类型,该类型提供了一些格式化输出的方法。本包也提供了一个预定义的“标准”Logger,可以通过辅助函数Print[f|ln]、Fatal[f|ln]和Panic[f|ln]访问,比手工创建一个Logger对象更容易使用。Logger会打印每条日志信息的日期、时间,默认输出到标准错误。Fatal系列函数会在写入日志信息后调用os.Exit(1)。Panic系列函数会在写入日志信息后调用panic。

1. Logger 结构体

Logger 结构体定义:

Logger 类型表示一个记录日志的对象,它会生成一行行的输出写入一个io.Writer 接口。每一条日志操作会调用一次io.Writer 接口的Write 方法。Logger 类型的对象可以被多个线程安全的同时使用,它会保证对io.Writer 接口的顺序访问。

  • func New(out io.Writer, prefix string, flag int) *Logger // 创建一个Logger, out设置日志信息写入的目的地, prefix 添加到生成的每一条日志前面, flag定义日志的属性
  • func (l *Logger) Flags() int // Flags返回logger的输出选项
  • func (l *Logger) SetFlags(flag int) // SetFlags设置logger的输出选项
  • func (l *Logger) Prefix() string // Prefix返回logger的输出前缀
  • func (l *Logger) SetPrefix(prefix string) // SSetPrefix设置logger的输出前缀
  • func (l *Logger) Output(calldepth int, s string) error // 输出一条日志
  • func (l *Logger) Printf(format string, v ...interface{}) // 将生成的格式化字符串输出到out
  • func (l *Logger) Fatal(v ...interface{}) // Fatal等价于{l.Print(v…); os.Exit(1)}
  • func (l *Logger) Panic(v ...interface{}) // Panic等价于{l.Print(v…); panic(…)}

2. log 导出函数

log 导出函数基于预定义的 std 变量 var std = New(os.Stderr, "", LstdFlags) 实现。

  • func Flags() int // 获取预定义“标准”日志输出属性
  • func Prefix() string // 获取预定义“标准”日志前缀
  • func SetOutput(w io.Writer) // 设置预定义“标准”日志输出位置
  • func Print(v ...interface{}) // 打印一条标准日志

3. 代码示例

$GOPATH/src/github.com/ironxu/go_note/library/log/log.go 源码如下:

参考

可以关注我的微博了解更多信息: @刚刚小码农

Go 标准库介绍五: io

本文介绍Go 标准库 io 常用的导出函数,结构体和方法。

io 包提供了对 I/O 原语的基本接口,这些接口抽象出了泛用的函数并附加了一些操作。因为这些接口是对底层实现完全不同的低水平操作的包装,除非得到其它方面的通知,客户端不应假设它们是并发安全的。

1. io 包的接口

Reader

Reader 接口用于包装基本的读取方法。Read 方法读取 len(p) 字节数据写入p。它返回写入的字节数和遇到的任何错误。即使Read方法返回值 n < len(p),本方法在被调用时仍可能使用p的全部长度作为暂存空间。如果有部分可用数据,但不够len(p)字节,Read会返回可以读取到的数据,而不是等待更多数据。

Writer

Writer 接口用于包装基本的写入方法。Write 方法将 len(p) 字节数据从p写入底层的数据流。它会返回写入的字节数据(0 <= n <= len(p)) 和遇到的任何导致写入提取结束的错误。如果它返回的 n < len(p), Write必须返回非nil的错误。

type Closer

Closer 接口用于包装基本的关闭方法。

Seeker

Seeker 接口用于包装基本的移位方法。Seek方法设定下一次读写的位置:偏移量为offset,校准点由whence确定:0表示相对于文件起始;1表示相对于当前位置;2表示相对于文件结尾。Seek方法返回新的位置以及可能遇到的错误。

ReaderAt

ReaderAt 接口包装了基本的ReadAt方法。ReadAt从底层输入流的偏移量off位置读取len(p) 字节数据写入p, 它返回读取的字节数(0 <= n <= len(p)) 和遇到的任何错误。

ByteReader

ByteReader 是基本的ReadByte方法的包装。ReadByte读取输入中的单个字节并返回。如果没有字节可读取,会返回错误。

ByteScanner

ByteScanner 接口在基本的ReadByte 方法之外还添加了UnreadByte方法。UnreadByte方法让下一次调用ReadByte时返回之前调用ReadByte时返回的同一个字节。连续调用两次UnreadByte方法而中间没有调用ReadByte时,可能会导致错误。

ReaderFrom

ReaderFrom 接口包装了基本的ReadFrom方法。ReadFrom 方法从r 读取数据直到EOF或者遇到错误。返回值n是读取的字节数,执行时遇到的错误(EOF除外)也会被返回。

相应还有 write 类接口,不一一列举

1.1 LimitedReader

LimitedReader 从R中读取数据,但限制可以读取的数据的量为最多N 字节,每次调用Read方法都会更新N 以标记剩余可以读取的字节数。

  • func LimitReader(r Reader, n int64) LimitedReader // 创建一个 LimitedReader
  • func (l *LimitedReader) Read(p []byte) (n int, err error)

1.2 SectionReader

SectionReader 实现了对底层满足ReadAt 接口的输入流某个片段的Read、ReadAt、Seek方法。

  • func NewSectionReader(r ReaderAt, off int64, n int64) *SectionReader // 返回一个从r中的偏移量off处为起始,读取n个字节后以停止的SectionReader
  • func (s *SectionReader) Size() int64 // Size返回该片段的字节数

2. io 导出函数

  • func WriteString(w Writer, s string) (n int, err error) // 将字符串s的内容写入w中
  • func Copy(dst Writer, src Reader) (written int64, err error) // 将src的数据拷贝到dst,直到在src上到达EOF或发生错误。返回拷贝的字节数和遇到的第一个错误

$GOPATH/src/github.com/ironxu/go_note/library/io/io.go 源码如下:

参考

可以关注我的微博了解更多信息: @刚刚小码农