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

这篇具有很好参考价值的文章主要介绍了GO自研微服务框架-路由实现。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

路由实现

1.不用框架

不用框架的路由实现

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.路由实现

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)
   }
}

测试代码

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.实现分组路由

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)
   }

测试代码

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.支持不同的请求方式

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)
   }
}

测试代码

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.支持同一个路径不同请求方式

package main

import "net/http"

type Context struct {
   W http.ResponseWriter
   R *http.Request
}
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)
   }
}

测试代码

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自研微服务框架-路由实现,Go自研微服务框架,golang,微服务

实现代码

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
}

测试代码

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适配前缀树

实现代码

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)
	}
}

测试代码

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)
}
}文章来源地址https://www.toymoban.com/news/detail-818695.html


测试代码

```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()
}

到了这里,关于GO自研微服务框架-路由实现的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 14-RPC-自研微服务框架

    RPC 框架是分布式领域核心组件,也是微服务的基础。 RPC (Remote Procedure Call)全称是远程过程调用,相对于本地方法调用,在同一内存空间可以直接通过方法栈实现调用,远程调用则跨了不同的服务终端,并不能直接调用。 RPC框架 要解决的就是远程方法调用的问题,并且实

    2024年03月08日
    浏览(54)
  • [golang 微服务] 7. go-micro框架介绍,go-micro脚手架,go-micro结合consul搭建微服务案例

    上一节讲解了 GRPC微服务集群 + Consul集群 + grpc-consul-resolver 相关的案例,知道了微服务之间通信采用的 通信协议 ,如何实现 服务的注册和发现 ,搭建 服务管理集群 ,以及服务与服务之间的 RPC通信方式 ,具体的内容包括: protobuf协议 , consul 及 docker部署consul集群 , GRPC框架 的

    2024年02月09日
    浏览(42)
  • Golang 打包go项目部署到linux服务器

    我们可以在终端中输入以下代码: 然后就会生成main-linux的二进制可执行文件,然后我们就可以将main-linux放到服务器中的任一目录中,然后我们就可以执行以下命令运行。 这是我们在网上可以搜索到的方法,但是我相信很多人通过这个方法尝试后发现,它并不能运行。我相信

    2024年02月16日
    浏览(67)
  • Go-Gin框架 五、路由分组分文件

    Gin是一个用Go编写的HTTPweb框架。它是一个类似于martini但拥有更好性能的API框架, 优于httprouter,速度提高了近 40 倍。 点击此处访问Gin官方中文文档。 新建文件main.go,内容如下: 运行后访问: http://localhost:8000/ Gin支持加载HTML模板, 然后根据模板参数进行配置并返回相应的数

    2024年02月13日
    浏览(42)
  • CentOS 9 x64 使用 Nginx、Supervisor 部署 Go/Golang 服务

    在 CentOS 9 x64 系统上,可以通过以下步骤来部署 Golang 服务。 安装以下软件包: Golang:Golang 编程语言 Nginx:Web 服务器 Supervisor:进程管理工具 Git:版本控制工具 EPEL:扩展软件包 可以通过以下命令来安装: 为 Git 生成 SSH 密钥,以便于进行代码管理。可以通过以下命令来生成

    2024年02月12日
    浏览(55)
  • Golang 通过开源库 go-redis 操作 NoSQL 缓存服务器

    前置条件: 1、导入库: import ( \\\"github.com/go-redis/redis/v8\\\" ) 2、搭建哨兵模式集群 具体可以百度、谷歌搜索,网上现成配置教程太多了,不行还可以搜教程视频,跟着视频博主一步一个慢动作,慢慢整。 本文只介绍通过 “主从架构 / 哨兵模式” 访问的形式,这是因为,单个

    2024年01月23日
    浏览(51)
  • Go For Web:一篇文章带你用 Go 搭建一个最简单的 Web 服务、了解 Golang 运行 web 的原理

    本文作为解决如何通过 Golang 来编写 Web 应用这个问题的前瞻,对 Golang 中的 Web 基础部分进行一个简单的介绍。目前 Go 拥有成熟的 Http 处理包,所以我们去编写一个做任何事情的动态 Web 程序应该是很轻松的,接下来我们就去学习了解一些关于 Web 的相关基础,了解一些概念,

    2023年04月14日
    浏览(51)
  • Go 语言体系下的微服务框架选型: Dubbo-go

      一、Go 微服务体系发展与选型 随着微服务技术的快速发展,其在各个领域都形成了一系列事实标准,在 Kubernetes 和容器技术加持下,云原生微服务已经成为了主流解决方案。而 Go 语言作为云原生领域最受欢迎的开发语言,正被越来越多的企业作为微服务开发的首选语言,

    2023年04月10日
    浏览(43)
  • Go 语言体系下的微服务框架选型:Dubbo-go

    随着微服务技术的快速发展,其在各个领域都形成了一系列事实标准,在 Kubernetes 和容器技术加持下,云原生微服务已经成为了主流解决方案。而 Go 语言作为云原生领域最受欢迎的开发语言,正被越来越多的企业作为微服务开发的首选语言,其中比较流行的包括 Go-micro、Go

    2024年01月21日
    浏览(41)
  • go kratos 微服务框架(笔记一)

    1.简介 B站基于Golang实现的一个轻量级开源的面向微服务的框架. Kratos框架不限制您使用任何第三方库来进行项目开发,因此您可以根据喜好来选择库进行集成。我们也会逐步针对更多被广泛使用的第三方库开发插件。 2.官方文档 https://go-kratos.dev/docs/ 3.架构图 特性: APIs:协议

    2024年02月06日
    浏览(40)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包