golang的http分析
type Handler interface {
ServeHTTP(ResponseWriter, *Request)
}
type ResponseWriter interface {
}
func ListenAndServe(addr string, handler Handler)
再看下http包内的一个重要函数,Handle,可见,传入的是一个监听的http path,第二个参数是上述的handler.
func Handle(pattern string, handler Handler)
看一下如何使用的:
type ImpHandler struct {}
func (h ImpHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { // 实现方法
w.Write([]byte("haha"))
}
func main() {
http.Handle("/", ImpHandler{})
http.ListenAndServe(":12345", nil )
}
func Handle(pattern string, handler Handler) { DefaultServeMux.Handle(pattern, handler) }
type ServeMux struct {
mu sync.RWMutex
m map[string]muxEntry
hosts bool // whether any patterns contain hostnames
}
type muxEntry struct {
explicit bool
h Handler
pattern string
}
可见,http的path和对应的处理handler的关系以muxEntry维护在这个默认的hash表m中。http.Handle传入的两个参数以hash形式保存在内部的全局变量DefaultServeMux中。
func ListenAndServe(addr string, handler Handler) error {
server := &Server{Addr: addr, Handler: handler}
return server.ListenAndServe()
}
同样,ListenAndServe本身只是一个对外接口,内部也有相应对象Server进行封装。前面说过这个方法是处理连接层面的事,那么这个server就是tcp server的一个抽象。
func (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)})
}
可见,这里直接就监听TCP连接了。其中的ln是个Listener接口,代码这样写比较漂亮:
// Multiple goroutines may invoke methods on a Listener simultaneously.
type Listener interface {
// Accept waits for and returns the next connection to the listener.
Accept() (Conn, error) // Close closes the listener.
// Any blocked Accept operations will be unblocked and return errors.
Close() error // Addr returns the listener's network address.
Addr() Addr
} // 这里实现得比较好,覆盖了一个Accept方法,在其中加入了keepAlived的选项。其他两个方法仍旧使用原listener的
type tcpKeepAliveListener struct {
*net.TCPListener // 外层可直接调它的方法不需要指定成员
} func (ln tcpKeepAliveListener) Accept() (c net.Conn, err error) {
tc, err := ln.AcceptTCP()
if err != nil {
return
}
tc.SetKeepAlive(true)
tc.SetKeepAlivePeriod( * time.Minute)
return tc, nil
}
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 failure
if err := srv.setupHTTP2_Serve(); err != nil {
return err
}
////////////////skip
for {
rw, e := l.Accept() // 取出一个连接,对应accept
if e != nil {
if ne, ok := e.(net.Error); ok && ne.Temporary() {
if tempDelay == {
tempDelay = * time.Millisecond
} else {
tempDelay *=
}
if max := * time.Second; tempDelay > max {
tempDelay = max
}
srv.logf("http: Accept error: %v; retrying in %v", e, tempDelay)
time.Sleep(tempDelay)
continue
}
return e
}
tempDelay =
c := srv.newConn(rw)
c.setState(c.rwc, StateNew) // before Serve can return
go c.serve(ctx)
}
}
可见,调用Listener的Accept()后,形成一个抽象的连接,再启单独协程去处理它。
serverHandler{c.server}.ServeHTTP(w, w.req)
看下面,最终回调回去了。
// serverHandler delegates to either the server's Handler or
// DefaultServeMux and also handles "OPTIONS *" requests.
type serverHandler struct {
srv *Server
} func (sh serverHandler) ServeHTTP(rw ResponseWriter, req *Request) {
handler := sh.srv.Handler
if handler == nil {
handler = DefaultServeMux
}
if req.RequestURI == "*" && req.Method == "OPTIONS" {
handler = globalOptionsHandler{}
}
handler.ServeHTTP(rw, req)
} // 最终回到最开始注册Handle的地方,进行ServeHTTP的调用
func (mux *ServeMux) ServeHTTP(w ResponseWriter, r *Request) {
if r.RequestURI == "*" {
if r.ProtoAtLeast(, ) {
w.Header().Set("Connection", "close")
}
w.WriteHeader(StatusBadRequest)
return
}
h, _ := mux.Handler(r)
h.ServeHTTP(w, r)
}
func main() {
fmt.Println("Hello.")
http.HandleFunc("/", func (w http.ResponseWriter, r *http.Request) {
w.Write([]byte("haha2"))
})
http.ListenAndServe(":12346", nil )
}
其中,func可使用闭包也可不用。
// HandleFunc registers the handler function for the given pattern.
func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
mux.Handle(pattern, HandlerFunc(handler))
} // HandleFunc registers the handler function for the given pattern
// in the DefaultServeMux.
// The documentation for ServeMux explains how patterns are matched.
func HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
DefaultServeMux.HandleFunc(pattern, handler)
}
可见,HandlerFunc只是对Handler的封装,下面同样是通过DefaultServeMux来进行。
// The HandlerFunc type is an adapter to allow the use of
// ordinary functions as HTTP handlers. If f is a function
// with the appropriate signature, HandlerFunc(f) is a
// Handler that calls f.
type HandlerFunc func(ResponseWriter, *Request) // ServeHTTP calls f(w, r).
func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {
f(w, r)
}
实际上的效果是,明明只写了一个函数func(ResponseWriter, *Request),但其他代码却可以通过golang的隐式接口方式通过另一个你不知道的函数调用你!这里,不知道的函数就是ServeHTTP。
func NewServeMux() *ServeMux { return new(ServeMux) }
这个ServeMux,本身又是隐式实现了Handler。
func (sh serverHandler) ServeHTTP(rw ResponseWriter, req *Request) {
handler := sh.srv.Handler
if handler == nil {
handler = DefaultServeMux
}
if req.RequestURI == "*" && req.Method == "OPTIONS" {
handler = globalOptionsHandler{}
}
handler.ServeHTTP(rw, req)
}
总结下:
golang的http分析的更多相关文章
- golang 性能优化分析:benchmark 结合 pprof
前面 2 篇 golang 性能优化分析系列文章: golang 性能优化分析工具 pprof (上) golang 性能优化分析工具 pprof (下) 一.基准测试 benchmark 简介 在 ...
- golang https server分析
https: HTTPS是http安全版本的实现,在http与tcp之间加了一层ssl/tls安全传输协议 为了防止请求被监听.篡改.冒充,在tls实现过程中引入了数字证书机制,数字证书由第三方权威机 ...
- 基于Golang的逃逸分析(Language Mechanics On Escape Analysis)
何为逃逸分析 在编译程序优化理论中,逃逸分析是一种确定指针动态范围的方法——分析在程序的哪些地方可以访问到指针.它涉及到指针分析和形状分析. 当一个变量(或对象)在子程序中被分配时,一个指向变量的指针 ...
- Golang源码分析之目录详解
开源项目「go home」聚焦Go语言技术栈与面试题,以协助Gopher登上更大的舞台,欢迎go home~ 导读 学习Go语言源码的第一步就是了解先了解它的目录结构,你对它的源码目录了解多少呢? 目 ...
- golang http server分析(一)
http:http请求过程实质上是一个tcp连接通信,具体通过socket接口编码实现 在go中是通过listenAndServer()方法对外提供了一个http服务,在该方法中完成了socket的通 ...
- golang标准库分析之net/rpc
net/rpc是golang提供的一个实现rpc的标准库.
- golang socket 实现分析(一)
socket:tcp/udp.ip构成了网络通信的基石,tcp/ip是面向连接的通信协议 要求建立连接时进行3次握手确保连接已被建立,关闭连接时需要4次通信来保证客户端和,服务端都已经关闭 在通信过程 ...
- golang内存对齐分析(转载)
问题 type Part1 struct { a bool b int32 c int8 d int64 e byte } 在开始之前,希望你计算一下 Part1 共占用的大小是多少呢? func m ...
- Golang闭包案例分析与普通函数对比
闭包案例 package main import ( "fmt" "strings" //记住一定引入strings包 ) //①编写一个函数makeSuffi ...
随机推荐
- Javascript面对对象. 第五篇
继承 继承是面向对象中一个核心的概念.其他正统面向对象语言都会用两种方式实现继承: 一个是接口实现,一个是继承. 而ECMAScript只支持继承,不支持接口实现,而实现继承的方式依靠原型链完成. / ...
- 解析jQuery中extend方法--源码解析以及递归的过程《二》
源码解析 在解析代码之前,首先要了解extend函数要解决什么问题,以及传入不同的参数,会达到怎样的效果.extend函数内部处理传入的不同参数,返回处理后的对象. extend函数用来扩展对象,增加 ...
- matlab分割背景与物体
[name,path]=uigetfile('*.jpg;*.png;*.bmp','选择一张图片');f=imread([path name]);count = 0; T = mean2(f); d ...
- 简单C程序,迷宫
#include<stdio.h> #include <stdlib.h> int visit(int, int); ][] = { {, , , , , , , , ,}, ...
- STM32按键控制程序
由于最近时间比较匆忙 已经有很久的时间没有写博客了 这次和大家分享的是STM32的一个按键的小程序 他的优点呢也是和上面一个LED一样就是便于移植 更改管脚方便 虽然都是些小程序 但是我觉得他们就像基 ...
- C语言中的函数、数组与指针
1.函数:当程序很小的时候,我们可以使用一个main函数就能搞定,但当程序变大的时候,就超出了人的大脑承受范围,逻辑不清了,这时候就需要把一个大程序分成许多小的模块来组织,于是就出现了函数概念: 函 ...
- Oracle 生成一张测试表并插入随机数据
--生成随机表 --CREATE table scott.One_Million as ( SELECT ROWNUM AS T_ID, TRUNC(DBMS_RANDOM.VALUE(, )) 年龄 ...
- java 抽象(abstract) 构造 静态(static) 总结--2017-03-02
抽象类:不能实例化!子类继承抽象类, 实例化子类对象才可以调用, 多态的体现; 抽象方法:必须被重写才能被调用; 静态方法:类名直接调用,或者实例化对象调用; 构造方法:new后面的括号里面带参数,就 ...
- 自定义view(二)
这里是自定义view(二),上一篇关于自定义view的一些基本知识,比如说自定义view的步骤.会涉及到哪些函数以及如何实现自定义属性,同时实现了一个很基础的自定义控件,一个自定义的计时器,需要看的人 ...
- JAVA开发环境搭建 - JDK安装及环境变量配置
1.前提条件 系统:本教程以WIN7系统为例 JDK:本教程以jdk-7u79-windows-x64为例 2.安装步骤 双击运行JDK安装程序