GO自研微服务框架-路由实现

路由实现

1.不用框架

不用框架的路由实现

go 复制代码
package main

import (
   "fmt"
   "log"
   "net/http"
)

func main() {
   http.HandleFunc("/hello", func(writer http.ResponseWriter, request *http.Request) {
      fmt.Fprintf(writer, "%s 欢迎来到goweb教程", "lisus.com")
   })
   err := http.ListenAndServe(":8111", nil)
   if err != nil {
      log.Fatal(err)
   }
}

2.路由实现

go 复制代码
package main

import (
   "log"
   "net/http"
)

type HandleFunc func(w http.ResponseWriter, r *http.Request)
//定义路由结构体
type router struct {
    //定义一个路由处理函数map
   handleFuncMap map[string]HandleFunc
}
//定义引擎
type Engine struct {
   router
}

func (r *router) Add(name string, handleFunc HandleFunc) {
   r.handleFuncMap[name] = handleFunc
}

func New() *Engine {
   return &Engine{
      router: router{handleFuncMap: make(map[string]HandleFunc)},
   }
}
func (e *Engine) Run() {
    //循环处理路由
   for key, value := range e.handleFuncMap {
      http.HandleFunc(key, value)
   }
   err := http.ListenAndServe(":8111", nil)
   if err != nil {
      log.Fatal(err)
   }
}

测试代码

go 复制代码
package main

import (
   "fmt"
   "net/http"
)

func main() {
   engine := New()
   engine.Add("/hello", func(w http.ResponseWriter, r *http.Request) {
      fmt.Fprintf(w, "%s 欢迎学习GO自研框架", "lisus2000")
   })
   engine.Run()
}

3.实现分组路由

go 复制代码
package main

import (
   "log"
   "net/http"
)

type HandleFunc func(w http.ResponseWriter, r *http.Request)

// 定义路由分组结构
type routerGroup struct {
   name          string
   handleFuncMap map[string]HandleFunc
}

type router struct {
   routerGroups []*routerGroup
}

// Group 分组方法
func (r *router) Group(name string) *routerGroup {
   routerGroup := &routerGroup{
      name:          name,
      handleFuncMap: make(map[string]HandleFunc),
   }
   r.routerGroups = append(r.routerGroups, routerGroup)
   return routerGroup
}

// Add 添加路由
func (r *routerGroup) Add(name string, handleFunc HandleFunc) {
   r.handleFuncMap[name] = handleFunc
}

type Engine struct {
   router
}

func New() *Engine {
   return &Engine{
      router: router{},
   }
}
func (e *Engine) Run() {
   //user key:get value func
   for _, group := range e.routerGroups {
      for key, value := range group.handleFuncMap {
         http.HandleFunc("/"+group.name+key, value)
      }
   }

   err := http.ListenAndServe(":8111", nil)
   if err != nil {
      log.Fatal(err)
   }

测试代码

go 复制代码
package main

import (
   "fmt"
   "net/http"
)

func main() {
   engine := New()
   g := engine.Group("user")
   g.Add("/hello", func(w http.ResponseWriter, r *http.Request) {
      fmt.Fprintf(w, "%s 欢迎学习GO自研框架", "lisus2000")
   })
   g.Add("/info", func(w http.ResponseWriter, r *http.Request) {
      fmt.Fprintf(w, "%s info功能", "lisus2000")
   })
   engine.Run()
}

4.支持不同的请求方式

go 复制代码
package main

import (
   "fmt"
   "log"
   "net/http"
)

type HandleFunc func(w http.ResponseWriter, r *http.Request)

// 定义路由分组结构
type routerGroup struct {
   name             string
   handleFuncMap    map[string]HandleFunc
   handlerMethodMap map[string][]string
}

type router struct {
   routerGroups []*routerGroup
}

// Group 分组方法
func (r *router) Group(name string) *routerGroup {
   routerGroup := &routerGroup{
      name:             name,
      handleFuncMap:    make(map[string]HandleFunc),
      handlerMethodMap: make(map[string][]string),
   }
   r.routerGroups = append(r.routerGroups, routerGroup)
   return routerGroup
}

// Add 添加路由
func (r *routerGroup) Add(name string, handleFunc HandleFunc) {
   r.handleFuncMap[name] = handleFunc
}
func (r *routerGroup) Any(name string, handleFunc HandleFunc) {
   r.handleFuncMap[name] = handleFunc
   r.handlerMethodMap["ANY"] = append(r.handlerMethodMap["ANY"], name)
}
func (r *routerGroup) Get(name string, handleFunc HandleFunc) {
   r.handleFuncMap[name] = handleFunc
   r.handlerMethodMap[http.MethodGet] = append(r.handlerMethodMap[http.MethodGet], name)
}
func (r *routerGroup) Post(name string, handleFunc HandleFunc) {
   r.handleFuncMap[name] = handleFunc
   r.handlerMethodMap[http.MethodPost] = append(r.handlerMethodMap[http.MethodPost], name)
}

type Engine struct {
   router
}

func New() *Engine {
   return &Engine{
      router: router{},
   }
}
func (e *Engine) ServeHTTP(w http.ResponseWriter, r *http.Request) {
   method := r.Method
   for _, group := range e.routerGroups {
      for name, methodHandle := range group.handleFuncMap {
         url := "/" + group.name + name
         if r.RequestURI == url {
            routers, ok := group.handlerMethodMap["ANY"]
            if ok {
               for _, routerName := range routers {
                  if routerName == name {
                     methodHandle(w, r)
                     return
                  }
               }
            }
            //method 进行匹配
            routers, ok = group.handlerMethodMap[method]
            if ok {
               for _, routerName := range routers {
                  if routerName == name {
                     methodHandle(w, r)
                     return
                  }
               }
            }
            w.WriteHeader(http.StatusMethodNotAllowed)
            fmt.Fprintf(w, "%s %s not allow\n", r.RequestURI, method)
            return
         }
      }
   }
   w.WriteHeader(http.StatusNotFound)
   fmt.Fprintf(w, "%s not found\n", r.RequestURI)
}
func (e *Engine) Run() {
   http.Handle("/", e)
   err := http.ListenAndServe(":8111", nil)
   if err != nil {
      log.Fatal(err)
   }
}

测试代码

go 复制代码
package main

import (
   "fmt"
   "net/http"
)

func main() {
   engine := New()
   g := engine.Group("user")
   g.Get("/hello", func(w http.ResponseWriter, r *http.Request) {
      fmt.Fprintf(w, "%s 欢迎学习GO自研框架", "lisus2000")
   })
   g.Post("/info", func(w http.ResponseWriter, r *http.Request) {
      fmt.Fprintf(w, "%s info功能", "lisus2000")
   })
   engine.Run()
}

5.支持同一个路径不同请求方式

go 复制代码
package main

import "net/http"

type Context struct {
   W http.ResponseWriter
   R *http.Request
}
go 复制代码
package main

import (
   "fmt"
   "log"
   "net/http"
)

const ANY = "ANY"

type HandleFunc func(ctx *Context)

// 定义路由分组结构
type routerGroup struct {
   name             string
   handleFuncMap    map[string]map[string]HandleFunc
   handlerMethodMap map[string][]string
}

type router struct {
   routerGroups []*routerGroup
}

// Group 分组方法
func (r *router) Group(name string) *routerGroup {
   routerGroup := &routerGroup{
      name:             name,
      handleFuncMap:    make(map[string]map[string]HandleFunc),
      handlerMethodMap: make(map[string][]string),
   }
   r.routerGroups = append(r.routerGroups, routerGroup)
   return routerGroup
}

func (r *routerGroup) handle(name string, method string, handleFunc HandleFunc) {
   _, ok := r.handleFuncMap[name]
   if !ok {
      r.handleFuncMap[name] = make(map[string]HandleFunc)
   }
   _, ok = r.handleFuncMap[name][method]
   if ok {
      panic("有重复的路由")
   }
   r.handleFuncMap[name][method] = handleFunc
   //r.handlerMethodMap[method] = append(r.handlerMethodMap[method], name)
}

func (r *routerGroup) Any(name string, handleFunc HandleFunc) {
   r.handle(name, ANY, handleFunc)
}
func (r *routerGroup) Get(name string, handleFunc HandleFunc) {
   r.handle(name, http.MethodGet, handleFunc)
}
func (r *routerGroup) Post(name string, handleFunc HandleFunc) {
   r.handle(name, http.MethodPost, handleFunc)
}

type Engine struct {
   router
}

func New() *Engine {
   return &Engine{
      router: router{},
   }
}
func (e *Engine) ServeHTTP(w http.ResponseWriter, r *http.Request) {
   method := r.Method
   for _, group := range e.routerGroups {
      for name, methodHandle := range group.handleFuncMap {
         url := "/" + group.name + name
         if r.RequestURI == url {
            ctx := &Context{
               W: w,
               R: r,
            }
            handle, ok := methodHandle[ANY]
            if ok {
               handle(ctx)
               return
            }

            handle, ok = methodHandle[method]
            if ok {
               handle(ctx)
               return
            }
            //method 进行匹配
            w.WriteHeader(http.StatusMethodNotAllowed)
            fmt.Fprintf(w, "%s %s not allow\n", r.RequestURI, method)
            return

         }
      }
   }
   w.WriteHeader(http.StatusNotFound)
   fmt.Fprintf(w, "%s not found\n", r.RequestURI)
}
func (e *Engine) Run() {
   http.Handle("/", e)
   err := http.ListenAndServe(":8111", nil)
   if err != nil {
      log.Fatal(err)
   }
}

测试代码

go 复制代码
package main

import (
   "fmt"
)

func main() {
   engine := New()
   g := engine.Group("user")
   g.Get("/hello", func(ctx *Context) {
      fmt.Fprintf(ctx.W, "%s Get欢迎学习GO自研框架", "lisus2000")
   })
   g.Post("/hello", func(ctx *Context) {
      fmt.Fprintf(ctx.W, "%s Post欢迎学习GO自研框架", "lisus2000")
   })
   g.Post("/info", func(ctx *Context) {
      fmt.Fprintf(ctx.W, "%s info功能", "lisus2000")
   })
   engine.Run()
}

6.前缀树

在前面实现的时候,我们的路径匹配实现的很简陋,不能实现更为复杂的需求,比如/user/get/:id 这种带有参数的,这种带有参数的路径,我们称之为动态路由

除了带有参数的,一般情况下,我们可能还希望支持通配符**,比如/static/**, 可以匹配/static/vue.js或者/static/css/index.css这些。

实现代码

go 复制代码
package msgo

import "strings"

type treeNode struct {
	name       string
	children   []*treeNode
	routerName string
	isEnd      bool
}

//put path: /user/get/:id

func (t *treeNode) Put(path string) {
	root := t
	strs := strings.Split(path, "/")
	for index, name := range strs {
		if index == 0 {
			continue
		}
		children := t.children
		isMatch := false
		for _, node := range children {
			if node.name == name {
				isMatch = true
				t = node
				break
			}
		}
		if !isMatch {
			isEnd := false
			if index == len(strs)-1 {
				isEnd = true
			}
			node := &treeNode{name: name, children: make([]*treeNode, 0), isEnd: isEnd}
			children = append(children, node)
			t.children = children
			t = node
		}

	}
	t = root
}

//get path:/user/get/11
// hello

func (t *treeNode) Get(path string) *treeNode {
	strs := strings.Split(path, "/")
	routerName := ""
	for index, name := range strs {
		if index == 0 {
			continue
		}
		children := t.children
		isMatch := false
		for _, node := range children {
			if node.name == name || node.name == "*" ||
				strings.Contains(node.name, ":") {

				isMatch = true
				routerName += "/" + node.name
				node.routerName = routerName
				t = node
				//到达最尾部结点
				if index == len(strs)-1 {
					return node
				}
				break
			}
		}
		if !isMatch {
			for _, node := range children {
				// /user/**
				// /user/get/userinfo
				// /user/aa/bb
				if node.name == "**" {
					routerName += "/" + node.name
					node.routerName = routerName
					return node
				}
			}
		}
	}
	return nil
}

测试代码

go 复制代码
package msgo

import (
   "fmt"
   "testing"
)

func TestTreeNode(t *testing.T) {
   root := &treeNode{
      name:     "/",
      children: make([]*treeNode, 0),
   }
   root.Put("/user/get/:id")
   root.Put("/user/create/hello")
   root.Put("/user/create/aaa")
   root.Put("/order/get/aaa")

   node := root.Get("/user/get/1")
   fmt.Println(node)

   node = root.Get("/user/create/hello")
   fmt.Println(node)

   node = root.Get("/user/create/aaa")
   fmt.Println(node)

   node = root.Get("/order/get/aaa")
   fmt.Println(node)

}

6.1适配前缀树

实现代码

go 复制代码
package msgo

import (
	"fmt"
	"log"
	"net/http"
)

const ANY = "ANY"

type HandleFunc func(ctx *Context)

// 定义路由分组结构
type routerGroup struct {
	name             string
	handleFuncMap    map[string]map[string]HandleFunc
	handlerMethodMap map[string][]string
	treeNode         *treeNode
}

type router struct {
	routerGroups []*routerGroup
}

// Group 分组方法
func (r *router) Group(name string) *routerGroup {
	routerGroup := &routerGroup{
		name:             name,
		handleFuncMap:    make(map[string]map[string]HandleFunc),
		handlerMethodMap: make(map[string][]string),
		treeNode:         &treeNode{name: "/", children: make([]*treeNode, 0)},
	}
	r.routerGroups = append(r.routerGroups, routerGroup)
	return routerGroup
}

func (r *routerGroup) handle(name string, method string, handleFunc HandleFunc) {
	_, ok := r.handleFuncMap[name]
	if !ok {
		r.handleFuncMap[name] = make(map[string]HandleFunc)
	}
	_, ok = r.handleFuncMap[name][method]
	if ok {
		panic("有重复的路由")
	}
	r.handleFuncMap[name][method] = handleFunc
	r.treeNode.Put(name)
}

func (r *routerGroup) Any(name string, handleFunc HandleFunc) {
	r.handle(name, ANY, handleFunc)
}
func (r *routerGroup) Get(name string, handleFunc HandleFunc) {
	r.handle(name, http.MethodGet, handleFunc)
}
func (r *routerGroup) Post(name string, handleFunc HandleFunc) {
	r.handle(name, http.MethodPost, handleFunc)
}
func (r *routerGroup) Delete(name string, handlerFunc HandleFunc) {
	r.handle(name, http.MethodDelete, handlerFunc)
}
func (r *routerGroup) Put(name string, handlerFunc HandleFunc) {
	r.handle(name, http.MethodPut, handlerFunc)
}
func (r *routerGroup) Patch(name string, handlerFunc HandleFunc) {
	r.handle(name, http.MethodPatch, handlerFunc)
}
func (r *routerGroup) Options(name string, handlerFunc HandleFunc) {
	r.handle(name, http.MethodOptions, handlerFunc)
}
func (r *routerGroup) Head(name string, handlerFunc HandleFunc) {
	r.handle(name, http.MethodHead, handlerFunc)
}

type Engine struct {
	router
}

func New() *Engine {
	return &Engine{
		router: router{},
	}
}
func (e *Engine) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	method := r.Method
	for _, group := range e.routerGroups {
		routerName := SubStringLast(r.RequestURI, "/"+group.name)
		// get/1
		node := group.treeNode.Get(routerName)
		if node != nil && node.isEnd {
			//路由匹配上了
			ctx := &Context{
				W: w,
				R: r,
			}
			handle, ok := group.handleFuncMap[node.routerName][ANY]
			if ok {
				handle(ctx)
				return
			}

			handle, ok = group.handleFuncMap[node.routerName][method]
			if ok {
				handle(ctx)
				return
			}
			//method 进行匹配
			w.WriteHeader(http.StatusMethodNotAllowed)
			fmt.Fprintf(w, "%s %s not allow\n", r.RequestURI, method)
			return
		}
	}
	w.WriteHeader(http.StatusNotFound)
	fmt.Fprintf(w, "%s not found\n", r.RequestURI)
}
func (e *Engine) Run() {
	http.Handle("/", e)
	err := http.ListenAndServe(":8111", nil)
	if err != nil {
		log.Fatal(err)
	}
}

测试代码

go 复制代码
package main

import (
	"fmt"
	"msgo"
)

func main() {
	engine := msgo.New()
	g := engine.Group("user")
	//g.Get("/hello", func(ctx *msgo.Context) {
	//	fmt.Fprintf(ctx.W, "%s Get欢迎学习GO自研框架", "lisus2000")
	//})
	g.Get("/hello/get", func(ctx *msgo.Context) {
		fmt.Fprintf(ctx.W, "%s Get欢迎学习GO自研框架", "lisus2000")
	})
	g.Post("/hello", func(ctx *msgo.Context) {
		fmt.Fprintf(ctx.W, "%s Post欢迎学习GO自研框架", "lisus2000")
	})
	g.Post("/info", func(ctx *msgo.Context) {
		fmt.Fprintf(ctx.W, "%s info功能", "lisus2000")
	})
	g.Get("/get/:id", func(ctx *msgo.Context) {
		fmt.Fprintf(ctx.W, "%s get user 路径变量的值", "lisus2000")
	})
	engine.Run()
}

il)

if err != nil {

log.Fatal(err)

}

}

复制代码
测试代码

```go
package main

import (
	"fmt"
	"msgo"
)

func main() {
	engine := msgo.New()
	g := engine.Group("user")
	//g.Get("/hello", func(ctx *msgo.Context) {
	//	fmt.Fprintf(ctx.W, "%s Get欢迎学习GO自研框架", "lisus2000")
	//})
	g.Get("/hello/get", func(ctx *msgo.Context) {
		fmt.Fprintf(ctx.W, "%s Get欢迎学习GO自研框架", "lisus2000")
	})
	g.Post("/hello", func(ctx *msgo.Context) {
		fmt.Fprintf(ctx.W, "%s Post欢迎学习GO自研框架", "lisus2000")
	})
	g.Post("/info", func(ctx *msgo.Context) {
		fmt.Fprintf(ctx.W, "%s info功能", "lisus2000")
	})
	g.Get("/get/:id", func(ctx *msgo.Context) {
		fmt.Fprintf(ctx.W, "%s get user 路径变量的值", "lisus2000")
	})
	engine.Run()
}
相关推荐
stark张宇7 天前
微服务架构必备:Gin + gRPC + Consul + Nacos + GORM 打造用户服务
微服务·gin·grpc
阿里云云原生11 天前
MSE Nacos Prompt 管理:让 AI Agent 的核心配置真正可治理
微服务·云原生
阿里云云原生11 天前
阿里云微服务引擎 MSE 及 API 网关 2026 年 1 月产品动态
微服务
花酒锄作田11 天前
Gin 框架中的规范响应格式设计与实现
golang·gin
麦聪聊数据11 天前
统一 Web SQL 平台如何收编企业内部的“野生数据看板”?
数据库·sql·低代码·微服务·架构
qwfys20012 天前
How to install golang 1.26.0 to Ubuntu 24.04
ubuntu·golang·install
云司科技codebuddy12 天前
技术支持过硬Trae核心代理
大数据·运维·python·微服务
codeejun12 天前
每日一Go-25、Go语言进阶:深入并发模式1
开发语言·后端·golang
递归尽头是星辰12 天前
微服务事务分级治理:从 Seata 全模式到 TDSQL 实战
微服务·云原生·架构·分布式事务·事务分级治理
没有bug.的程序员12 天前
订单系统重构史诗:从单体巨兽到微服务矩阵的演进、数据一致性内核与分布式事务
java·微服务·矩阵·重构·分布式事务·数据一致性·订单系统