目录:
1: 中间件的意义
2: 实现原理
3: 代码实现
4: 围绕中间件扩展功能(日志、统计、限流等等)
1、中间件意义
避免代码中出现多重嵌套if(如下), 提升复杂度。
func demo(){
if condition1{
if condition2{
if condition3{
if condition4{
if condition5{
...
}
}
}
}
}
}
- 提高复用、隔离业务。
- 调用清晰、组合随意。
2、实现原理
洋葱结构、每一层都是一个中间件。
3、代码实现
中间件一般都封装在路由上,路由是URL请求分发的管理器
中间件选型: - 基于链表构建中间件:缺点是实现复杂、调用方式不灵活
- 使用数组构建中间件:控制灵活方便,推荐使用。例如Gin也是基于该思路构建
3.1、使用方法数组构建中间件
实现思路:
(1) 方法组装
- 构建中间件URL路由
- 构建URL的中间件方法数组
- 使用use方法整合路由与方法数组
(2) 方法回调
- 构建方法请求逻辑
- 封装http.Handler接口与http.Server整合
3.2 基础案例: 使用数组构建的中间件实现一个HTTP服务
目录结构
- demo
- middleware
- slice_router.go
- tracelog_slicemw.go
- main.go
日志中间件(middleware/tracelog_slicemw.go)
package middleware
import "log"
func TraceLogSliceMW() func(c *SliceRouterContext) {
return func(c *SliceRouterContext) {
log.Println("trace_in")
c.Next()
log.Println("trace_out")
}
}
路由器
package middleware
import (
"context"
"math"
"net/http"
"strings"
)
//目标定位是 tcp、http通用的中间件
const abortIndex int8 = math.MaxInt8 / 2 //最多 63 个中间件
type HandlerFunc func(*SliceRouterContext)
// router 结构体
type SliceRouter struct {
groups []*SliceGroup
}
// group 结构体
type SliceGroup struct {
*SliceRouter
path string
handlers []HandlerFunc
}
// router上下文
type SliceRouterContext struct {
Rw http.ResponseWriter
Req *http.Request
Ctx context.Context
*SliceGroup
index int8
}
func newSliceRouterContext(rw http.ResponseWriter, req *http.Request, r *SliceRouter) *SliceRouterContext {
newSliceGroup := &SliceGroup{
}
//最长url前缀匹配
matchUrlLen := 0
for _, group := range r.groups {
//fmt.Println("req.RequestURI")
//fmt.Println(req.RequestURI)
if strings.HasPrefix(req.RequestURI, group.path) {
pathLen := len(group.path)
if pathLen > matchUrlLen {
matchUrlLen = pathLen
*newSliceGroup = *group //浅拷贝数组指针
}
}
}
c := &SliceRouterContext{
Rw: rw, Req: req, SliceGroup: newSliceGroup, Ctx: req.Context()}
c.Reset()
return c
}
func (c *SliceRouterContext) Get(key interface{
}) interface{
} {
return c.Ctx.Value(key)
}
func (c *SliceRouterContext) Set(key, val interface{
}) {
c.Ctx = context.WithValue(c.Ctx, key, val)
}
type SliceRouterHandler struct {
coreFunc func(*SliceRouterContext) http.Handler
router *SliceRouter
}
func (w *SliceRouterHandler) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
c := newSliceRouterContext(rw, req, w.router)
if w.coreFunc != nil {
c.handlers = append(c.handlers, func(c *SliceRouterContext) {
w.coreFunc(c).ServeHTTP(rw, req)
})
}
c.Reset()
c.Next()
}
func NewSliceRouterHandler(coreFunc func(*SliceRouterContext) http.Handler, router *SliceRouter) *SliceRouterHandler {
return &SliceRouterHandler{
coreFunc: coreFunc,
router: router,
}
}
// 构造 router
func NewSliceRouter() *SliceRouter {
return &SliceRouter{
}
}
// 创建 Group
func (g *SliceRouter) Group(path string) *SliceGroup {
return &SliceGroup{
SliceRouter: g,
path: path,
}
}
// 构造回调方法
func (g *SliceGroup) Use(middlewares ...HandlerFunc) *SliceGroup {
g.handlers = append(g.handlers, middlewares...)
existsFlag := false
for _, oldGroup := range g.SliceRouter.groups {
if oldGroup == g {
existsFlag = true
}
}
if !existsFlag {
g.SliceRouter.groups = append(g.SliceRouter.groups, g)
}
return g
}
// 从最先加入中间件开始回调
func (c *SliceRouterContext) Next() {
c.index++
for c.index < int8(len(c.handlers)) {
//fmt.Println("c.index")
//fmt.Println(c.index)
c.handlers[c.index](c)
c.index++
}
}
// 跳出中间件方法
func (c *SliceRouterContext) Abort() {
c.index = abortIndex
}
// 是否跳过了回调
func (c *SliceRouterContext) IsAborted() bool {
return c.index >= abortIndex
}
// 重置回调
func (c *SliceRouterContext) Reset() {
c.index = -1
}
HTTP服务(main.go)
package main
var addr = "127.0.0.1:2002"
func main() {
log.Println("Starting httpserver at " + addr)
// 初始化(方法数组路由器)
sliceRouter := middleware.NewSliceRouter()
// 中间件可充当业务逻辑代码
sliceRouter.Group("/middle").Use(middleware.TraceLogSliceMW(), func(c *middleware.SliceRouterContext) {
c.Rw.Write([]byte("middleware func"))
})
routerHandler := middleware.NewSliceRouterHandler(nil, sliceRouter)
log.Fatal(http.ListenAndServe(addr, routerHandler))
3.3、在以上基础上扩展一个转发代理中间件
思路:
1.先启动后端服务。
2.创建一个代理(教你开发一个代理服务器)
3.创建一个中间件,将代理逻辑写入
4.路由器use该中间件
目录
- demo
- proxy // 新增代理
- proxt.go
- backend // 新增后端服务
- main.go
- middleware // 该目录下所有文件不变
- slice_router.go
- tracelog_slicemw.go
- main.go // 稍作修改
1.后端服务(backend/main.go)
package main
type RealServer struct {
Addr string
}
func (r *RealServer) HelloHandler(w http.ResponseWriter, req *http.Request) {
io.WriteString(w, fmt.Sprintf("hit %v", r.Addr))
}
func (r *RealServer) Run() {
log.Println("Starting httpserver at " + r.Addr)
mux := &http.ServeMux{
}
mux.HandleFunc("/", r.HelloHandler)
server := &http.Server{
Addr: r.Addr, WriteTimeout: time.Second * 3, Handler: mux,}
go func() {
log.Fatal(server.ListenAndServe())
}()
}
func main() {
rs1 := RealServer{
Addr: "127.0.0.1:2003"}
rs2 := RealServer{
Addr: "127.0.0.1:2004"}
rs1.Run()
rs2.Run()
//监听关闭信号
quit := make(chan os.Signal)
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
<-quit
}
- 代理(proxy/proxy.go)
待更新..
- 根函数(main.go)
package main
var addr = "127.0.0.1:2002"
func main() {
reverseProxy := func(c *middleware.SliceRouterContext) http.Handler {
url1, _ := url.Parse("http://127.0.0.1:2003")
url2, _ := url.Parse("http://127.0.0.1:2004")
_ = []*url.URL{
url1, url2}
return nil
}
log.Println("Starting httpserver at " + addr)
// 初始化方法数组路由器
sliceRouter := middleware.NewSliceRouter()
// 中间件可充当业务逻辑代码
sliceRouter.Group("/middle").Use(middleware.TraceLogSliceMW(), func(c *middleware.SliceRouterContext) {
c.Rw.Write([]byte("middleware func"))
})
// 中间件请求到反向代理
sliceRouter.Group("/proxt").Use(middleware.TraceLogSliceMW(), func(c *middleware.SliceRouterContext) {
fmt.Println("reverse proxy")
reverseProxy(c).ServeHTTP(c.Rw, c.Req)
})
routerHandler := middleware.NewSliceRouterHandler(nil, sliceRouter)
log.Fatal(http.ListenAndServe(addr, routerHandler))
}