go web编程——路由与http服务
本文主要讲解go语言web编程中的路由与http服务基本原理。
首先,使用go语言启动一个最简单的http服务:
package main
import (
"log"
"net/http"
)
func main() {
http.HandleFunc("/", sayHello)
log.Println("server running...")
log.Fatal(http.ListenAndServe("localhost:4000", nil))
}
func sayHello(writer http.ResponseWriter, req *http.Request) {
writer.Write([]byte("hello world!"))
}
编译,运行,浏览器访问 http://localhost:4000/ ,输出 hello world! 。
总的来说,这段代码只是做了两件事情,第一,注册路由,指定客户端请求路径对应的响应函数:
http.HandleFunc("/", sayHello)
第二,启动http服务,监听端口,接受并响应客户端请求:
http.ListenAndServe("localhost:4000", nil)
先看第一件事情——注册路由,指定请求路径对应的响应函数。
首先看 http.HandleFunc() 函数源码:
func HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
DefaultServeMux.HandleFunc(pattern, handler)
}
其中 DefaultServeMux 是go的默认路由器,所以注册路由实际上是由路由器进行的,http.HandleFunc() 函数只是对它进行封装,那么路由器的结构是怎么样的呢?
源码可见:
type ServeMux struct {
mu sync.RWMutex
m map[string]muxEntry
es []muxEntry
hosts bool
}
type muxEntry struct {
h Handler
pattern string
}
其中 ServeMux 结构中的 map[string]muxEntry 就是用来保存请求路径与响应函数之间的映射。从 muxEntry 结构定义可知,响应函数的类型为 Handler,而Handler实际上是一个接口类型,源码如下:
type Handler interface {
ServeHTTP(ResponseWriter, *Request)
}
所以,响应函数需要实现这个接口,才能进行路由注册。
源码中声明了一个 HandlerFunc 类型,就实现了 Handler 接口:
type HandlerFunc func(ResponseWriter, *Request)
func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {
f(w, r)
}
所以,只要我们的响应函数满足结构 func (http.ResponseWriter, *http.Request) ,即可进行路由注册,注册路由时,路由器会将其类型强制转换为 HandlerFunc 。其中,http.ResponseWriter参数包含了响应头、响应数据等响应相关信息,而http.Request参数则包含了请求头、请求参数等请求相关信息。
再看第二件事情,启动http服务,监听端口,接受并响应客户端请求。
首先看 http.ListenAndServe() 函数源码:
func ListenAndServe(addr string, handler Handler) error {
server := &Server{Addr: addr, Handler: handler}
return server.ListenAndServe()
}
其中 Server 即为http服务器类型,其结构如下(省略了部分字段):
type Server struct {
Addr string
Handler Handler
......
}
其中 Addr 为服务器监听的ip与端口字符串,Handler 为路由器,指定其为 nil 时,go会使用它的默认路由器 DefaultServeMux (调用 http.HandleFunc() 方法注册路由时就是注册到这个默认的路由器)。
服务器监听端口,接受客户端请求,并做出响应,这个过程可借助《go web编程》中的一张图示来帮助理解:
图中有两个红色矩形标记,第一个,说明针对客户端的每一个请求,go都会使用一个Goroutine进行响应,保证每个请求都能独立,相互不会阻塞,可以高效响应网络事件;第二,最终调用默认路由器的 ServeHTTP(w ResponseWriter, r *Request) 方法进行路由,从请求路径与响应函数的映射中找到对应的handler,最后调用handler的 ServeHTTP(w ResponseWriter, r *Request) 方法,从上面 HandlerFunc 类型的 ServeHTTP(w ResponseWriter, r *Request) 方法可知,其实最后调用的就是我们注册路由时定义的响应函数本身。
使用go默认路由器的不足之处是,不满足RESTful规则,而且对请求路径的路由只支持绝对匹配,不支持正则匹配。如果想设计一些特殊、简便的路由,需要设计一个自定义路由器,并让go的http服务器使用这个自定义路由器。关于自定义路由器的设计,可以参考笔者另一篇博文:
借鉴:
《Go Web编程》
原文地址:https://www.cnblogs.com/wujuntian/p/11858632.html
- 用 TensorFlow 让你的机器人唱首原创给你听
- 不用synchronized块的话如何实现一个原子的i++?
- Oracle升级中的参数补充(r9笔记第2天)
- 【Go 语言,服务器模块】日志系统源码
- wait方法和sleep方法的区别
- Java面试系列10
- python django整理(三)页面基础(仿BBS)
- 高并发场景下的httpClient优化使用
- socket.io 相关:Example: A simple chat server(官方 实例)
- django整理(四)配置setting文件(CSS,JS,images,templates)路径
- idea 远程调试 tomcat web应用
- Java 中冷门的 synthetic 关键字原理解读
- Spring 数据库连接(Connection)绑定线程(Thread)的实现
- Golang语言实现AzDG可逆加密算法实例
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- Spark中广播变量详解以及如何动态更新广播变量
- 按需加载 AngularJS 的 Controller
- Spark SQL中Not in Subquery为何低效以及如何规避
- 踩坑记 | Flutter升级影响了NestedScrollView?
- 使用 RequireJS 加载 AngularJS
- 通过Spark生成HFile,并以BulkLoad方式将数据导入到HBase
- 使用 C 创建 Windows 服务
- AngularJS 中的 controllerAs
- Android-Jetpack笔记-DataBinding
- Android-Jetpack笔记-Lifecycles
- Android-Jetpack笔记-LiveData
- Android-Jetpack笔记-ViewModelSavedState
- Android-Jetpack笔记-ViewModel
- GitLab CE 安装记录
- SparkSQL中产生笛卡尔积的几种典型场景以及处理策略