GO语言工程实践课后作业| 青训营

59 阅读4分钟

HTTP框架修炼之道课程作业(作业4&作业5)

作业内容

  1. 为什么HTTP框架做要分层设计?分层设计有哪些优势与劣势。
  2. 现有开源社区HTTP框架有哪些优势与不足。
  3. 中间件还有没有其他实现方式?可以用伪代码说明。
  4. 完成基于前缀路由树的注册与查找功能?可以用伪代码说明。
  5. 路由还有没有其他的实现方式?

作业4

完成基于前缀路由树的注册与查找功能?可以用伪代码说明

基于前缀路由树的注册与查找功能是一种常见的路由匹配算法,用于在一个路由表中查找与给定请求路径匹配的路由。它的主要思想是使用一棵前缀树来存储所有的路由信息。

一个前缀树由一个根节点、若干个内部节点和若干个叶子节点组成。每个节点都有一个值,通常是一个字符,用来表示当前节点的名称。内部节点没有值,叶子节点的值则是路由对应的处理函数。

// 节点结构体
type Node struct {
    Value    string
    Children map[string]*Node
    Handler  func(http.ResponseWriter, *http.Request)
}

// 前缀路由树结构体
type PrefixTree struct {
    Root *Node
}

在注册路由时,我们将路由按照路径分割成多个“路由段”,然后将路由段依次添加到前缀树中。如果当前节点没有对应名称的子节点,我们就创建一个新节点,并将路由段的值作为新节点的值。如果该节点已经存在,则直接跳到该节点。

// 注册路由
func (t *PrefixTree) RegisterRoute(route string, handler func(http.ResponseWriter, *http.Request)) {
    currentNode := t.Root
    segments := strings.Split(route, "/")

    for _, segment := range segments {
        if _, ok := currentNode.Children[segment]; !ok {
            newNode := &Node{
                Value:    segment,
                Children: make(map[string]*Node),
            }
            currentNode.Children[segment] = newNode
            currentNode = newNode
        } else {
            currentNode = currentNode.Children[segment]
        }
    }

    currentNode.Handler = handler
}

在查找路由时,我们将请求路径按照路径分割成多个“路径段”,然后遍历前缀树,逐个匹配路径段,直到找到最后一个路径段为止。如果在匹配过程中,某个节点的子节点中没有与当前路径段匹配的节点,则说明该路径并不存在。如果匹配成功,则返回该节点的值,即对应的处理函数。

// 查找路由
func (t *PrefixTree) FindRoute(route string) func(http.ResponseWriter, *http.Request) {
    currentNode := t.Root
    segments := strings.Split(route, "/")

    for _, segment := range segments {
        if _, ok := currentNode.Children[segment]; ok {
            currentNode = currentNode.Children[segment]
        } else {
            return nil // 路由不存在
        }
    }

    return currentNode.Handler
}

在使用时,我们可以创建一个前缀路由树对象,然后注册路由,并传入对应的处理函数。之后根据请求路径查找对应的处理函数,并进行处理。

// 创建前缀路由树
prefixTree := &PrefixTree{Root: &Node{Value: ""}}

// 注册路由
prefixTree.RegisterRoute("/api/user", userHandler)
prefixTree.RegisterRoute("/api/product", productHandler)
prefixTree.RegisterRoute("/api/product/detail", productDetailHandler)

// 查找路由并调用对应的处理函数
handler := prefixTree.FindRoute("/api/user") // 返回 userHandler
handler(responseWriter, request)             // 调用 userHandler 处理请求

handler = prefixTree.FindRoute("/api/product/detail") // 返回 productDetailHandler
handler(responseWriter, request)                       // 调用 productDetailHandler 处理请求

handler = prefixTree.FindRoute("/api/orders") // 返回 nil

作业5

路由还有没有其他的实现方式?

除了基于前缀路由树的实现方式,路由还可以使用正则表达式、哈希表等方式实现。下面简单介绍一下这几种方式。

  1. 正则表达式路由

使用正则表达式来匹配路由,常见的正则表达式匹配库有regexpre等。在Go语言中,常用的正则表达式路由库有muxhttprouter等。正则表达式路由的优点是可以处理各种复杂的路由匹配需求,例如通配符、参数等。缺点是性能可能较差,因为需要进行正则表达式匹配,而正则表达式的匹配复杂度较高。

  1. 哈希表路由

使用哈希表(或字典)来存储路由和对应的处理函数。在Go语言中,常用的哈希表路由库有chigin等。哈希表路由的优点是查找速度快,而且可以实现快速的动态路由注册和删除。缺点是无法处理复杂的路由匹配需求,例如通配符、参数等。

  1. 其他路由实现方式

还有一些其他的路由实现方式,例如使用有限状态机(FSM)来匹配路由,或者使用有向无环图(DAG)来存储路由和对应的处理函数。这些实现方式具有一些特殊的优点和适用场景,但是相对于前缀路由树和正则表达式路由来说较为复杂,一般情况下不太常用。