Golang HTTP编程及源码解析
1、网络基础
基本TCP客户-服务器程序Socket编程流程如如下图所示。
TCP服务器绑定到特定端口并阻塞监听客户端端连接,
TCP客户端则通过IP+端口向服务器发起请求,客户-服务器建立连接之后就能开始进行数据传输。
Golang的TCP编程也是基于上述流程的。
2、Golang HTTP编程
2.1 代码示例
func timeHandler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "%v", time.Now().Format(time.RFC3339))
}
func helloHandler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "%v", "hello world.")
}
func main() {
// 1. 新建路由解码器
h := http.NewServeMux()
// 2. 路由注册
h.HandleFunc("/hello", helloHandler)
h.HandleFunc("/time", timeHandler)
// 3. 服务启动 阻塞监听
http.ListenAndServe(":8000", h)
}
运行上述程序,在浏览器地址栏分别输入 http://localhost:8000/hello http://localhost:8000/time 结果分别如下图所示。
2.2 源码分析
分析从路由注册到响应用户请求的流程。
2.2.1 新建解码器 h := http.NewServeMux()
type ServeMux struct {
mu sync.RWMutex
m map[string]muxEntry
es []muxEntry // slice of entries sorted from longest to shortest.
hosts bool // whether any patterns contain hostnames
}
type muxEntry struct {
h Handler
pattern string
}
// NewServeMux allocates and returns a new ServeMux.
func NewServeMux() *ServeMux { return new(ServeMux) }
Handler是interface
,定义如下
type Handler interface {
ServeHTTP(ResponseWriter, *Request)
}
ServeMux
实现了Handler
接口。
2.2.2 路由注册 h.HandleFunc("/hello", helloHandler)
// HandleFunc registers the handler function for the given pattern.
func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
...
mux.Handle(pattern, HandlerFunc(handler))
}
func (mux *ServeMux) Handle(pattern string, handler Handler) {
...
e := muxEntry{h: handler, pattern: pattern}
mux.m[pattern] = e
if pattern[len(pattern)-1] == '/' {
mux.es = appendSorted(mux.es, e)
}
...
}
timeHandler
和helloHandler
函数被强制转换为type HandlerFunc func(ResponseWriter, *Request)
类型,且实现了Handler
接口。
func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {
f(w, r)
}
mux.m
建立了路由到处理函数timeHandler
和helloHandler
的映射。
2.2.3 服务启动阻塞监听 http.ListenAndServe(":8000", h)
包装Server
结构体,HTTP使用TCP协议。
func ListenAndServe(addr string, handler Handler) error {
server := &Server{Addr: addr, Handler: handler}
return server.ListenAndServe()
}
func (srv *Server) ListenAndServe() error {
...
ln, err := net.Listen("tcp", addr)
if err != nil {
return err
}
return srv.Serve(ln)
}
net.Listen
封装了Socket编程的socket
,bind
,listen
的调用,极大的方便了使用者。
阻塞监听请求,新建goroutine处理每个新请求。
func (srv *Server) Serve(l net.Listener) error {
...
for {
rw, err := l.Accept()
...
c := srv.newConn(rw)
c.setState(c.rwc, StateNew, runHooks) // before Serve can return
go c.serve(connCtx)
}
}
// Serve a new connection.
func (c *conn) serve(ctx context.Context) {
...
serverHandler{c.server}.ServeHTTP(w, w.req)
...
}
func (sh serverHandler) ServeHTTP(rw ResponseWriter, req *Request) {
handler := sh.srv.Handler
...
handler.ServeHTTP(rw, req)
}
通过前面的流程推导可知,handler是http.ListenAndServe
的第二个参数ServeMux
// ServeHTTP dispatches the request to the handler whose
// pattern most closely matches the request URL.
func (mux *ServeMux) ServeHTTP(w ResponseWriter, r *Request) {
...
h, _ := mux.Handler(r) // 通过路由获取处理函数
h.ServeHTTP(w, r)
}
mux.Handler
使用mux.m
这个map
通过请求URL找到对应处理函数的。
h的实际类型为HandlerFunc
,根据2.2.2会调用到具体函数timeHandler
或者helloHandler
。
3. 总结
golang对socket编程进行了封装,给HTTP编程带来了极大的便利。
但是不支持一下特性
1. 路由分组 对路由进行分组,可以方便分组鉴权
2. 动态路由 如动态路由/user/:username/post/:postid
不支持
Golang HTTP编程及源码解析的更多相关文章
- golang的flag包源码解析与使用
当我们 import package时,package内的全局常量和全局变量会进行初始化,并且紧接着init函数会执行.因此我们先看一下flag包的全局常量和全局变量. 一.flag包的全局常量.全 ...
- 【Java源码解析】Thread
简介 线程本质上也是进程.线程机制提供了在同一程序内共享内存地址空间运行的一组线程.对于内核来讲,它就是进程,只是该进程和其他一下进程共享某些资源,比如地址空间.在Java语言里,Thread类封装了 ...
- MyBatis源码解析【7】接口式编程
前言 这个分类比较连续,如果这里看不懂,或者第一次看,请回顾之前的博客 http://www.cnblogs.com/linkstar/category/1027239.html 修改例子 在我们实际 ...
- 【Android编程】android平台的MITM瑞士军刀_cSploit源码解析及中间人攻击复现
/文章作者:Kali_MG1937 作者博客ID:ALDYS4 QQ:3496925334 未经允许,禁止转载/ 何为MITM欺骗,顾名思义,中间人攻击的含义即为在局域网中充当数据包交换中间人的角色 ...
- Spring IoC源码解析——Bean的创建和初始化
Spring介绍 Spring(http://spring.io/)是一个轻量级的Java 开发框架,同时也是轻量级的IoC和AOP的容器框架,主要是针对JavaBean的生命周期进行管理的轻量级容器 ...
- 源码解析-Volley(转自codeKK)
Volley 源码解析 本文为 Android 开源项目源码解析 中 Volley 部分项目地址:Volley,分析的版本:35ce778,Demo 地址:Volley Demo分析者:grumoon ...
- jQuery整体架构源码解析(转载)
jQuery整体架构源码解析 最近一直在研读 jQuery 源码,初看源码一头雾水毫无头绪,真正静下心来细看写的真是精妙,让你感叹代码之美. 其结构明晰,高内聚.低耦合,兼具优秀的性能与便利的扩展性, ...
- String,StringBuffer和StringBuilder源码解析[基于JDK6]
最近指导几位新人,学习了一下String,StringBuffer和StringBuilder类,从反馈的结果来看,总体感觉学习的深度不够,没有读出东西.其实,JDK的源码是越读越有味的.下面总结一下 ...
- kubernetes源码解析---- apiserver路由构建解析(1)
kubernetes源码解析---- apiserver路由构建解析(1) apiserver作为k8s集群的唯一入口,内部主要实现了两个功能,一个是请求的路由和处理,简单说就是监听一个端口,把接收到 ...
- Retrofit2 源码解析
原文链接:http://bxbxbai.github.io/2015/12/13/retrofit2-analysis/ 公司里最近做的项目中网络框架用的就是Retrofit,用的多了以后觉得这个框架 ...
随机推荐
- Vue使用axios请求接口返回成功200但是进入到catch中
发生这个问题时查阅了许多资料,没有一个是对得上的.最后发现原来是在请求拦截器中的错误 错误代码如下 // 添加响应拦截器 axios.interceptors.response.use(functio ...
- MySQL DATE_SUB查询工龄大于35的员工信息
#(11) 查询工龄大于或等于35年的员工信息.SELECT * FROM emp e WHERE e.HIREDATE<=DATE_SUB(SYSDATE(),INTERVAL 35 YEAR ...
- MIT6.828 Lab 1: C, Assembly, Tools, and Bootstrapping
前置准备 实现机器为VMWare的虚拟机,操作系统为 Debian-11(无桌面版本),内核版本为 5.10.0,指令集为 AMD64(i7 9700K),编译器为 GCC-10 QEMU 虚拟化支持 ...
- Django基础笔记6(Django中间件)
Django自带的中间件 中间件执行流程 自定义中间件 Middle.py class Middle1(MiddlewareMixin): def process_request(self, requ ...
- 线上服务异常的定位、处理与优化的探索 - 第三章 Java虚拟机
Java虚拟机 之所以引入关于JVM的篇章,是发现多数项目发生的线上问题很大的几率源自JVM调优配置不当引起.对JVM的内存模型.GC垃圾回收机制.调优方式有一个系统化的了解后,可以快速处理或避免 ...
- v-if v-for同时使用 解决eslint报错问题
<template v-for="sec in item.goods"> <div v-if="item.showDetail" class= ...
- vba + ado +sql 连接数据库的常用操作方式
vba + ado +sql 连接Access.MySQL.Oracle Private Sub Connection_DBA() '********************************* ...
- BIO和NIO的区别和原理
BIO BIO(Blocking IO) 又称同步阻塞IO,一个客户端由一个线程来进行处理 当客户端建立连接后,服务端会开辟线程用来与客户端进行连接.以下两种情况会造成IO阻塞: 服务端会一直阻塞,直 ...
- nuxt.js框架 如何打包 build
nuxt脚手架开发好项目后怎么打包 以下是脚手架的package.json部分代码 "scripts": { "dev": "cross-env NO ...
- JavaScript:七大基础数据类型:字符串string
在JS中,字符串类型的数据,有三种表达方式,但是无一例外都是需要引号扩起来的: 单引号'hello' 双引号"hello" 反引号`hello` 注意: 因为JS没有字符类型,这一 ...