为你自己学Go
  • README
  • PART01.Web框架概览
    • 1.01 Web框架概览:学习路线
    • 1.02 Web框架概览-Beego框架分析
    • 1.03 Web框架概览-GIN框架分析
    • 1.04 Web框架概览-Iris框架分析
    • 1.05 Web框架概览-Echo框架分析
  • PART02.Server
    • 2.01 Server详解与面试要点
  • PART03.路由树
    • 3.01 路由树-Beego&GIN&Echo实现与设计总结
    • 3.02 路由树-全静态匹配
    • 3.03 路由树-TDD起步
    • 3.04 路由树-静态匹配测试用例
    • 3.05 路由树-静态匹配之路由查找
    • 3.06 路由树-静态匹配之集成Server
    • 3.07 路由树-通配符匹配之路由注册
    • 3.08 路由树-通配符匹配之路由查找与测试
    • 3.09 路由树-参数路径之基本注册和查找
    • 3.10 路由树-参数路径之校验
    • 3.11 路由树-参数路径之参数值
    • 3.12 路由树-总结与面试要点
  • PART04.课后复习
    • 4.01 课后复习-Server
    • 4.02 课后复习-Route
  • PART05.Context
    • 5.01 Context-简介
    • 5.02 Context-Beego Context设计分析
    • 5.03 Context-Gin Context设计分析
    • 5.04 Context-Echo和Iris的Context设计分析
    • 5.05 Context-处理输入输出总结
    • 5.06 Context-处理输入之Body输入
    • 5.07 Context-处理输入之表单输入
    • 5.08 Context-处理输入之查询参数、路径参数和StringValue
    • 5.09 Context-处理输出
    • 5.10 Context-总结与面试要点
  • PART06.AOP
    • 6.01 AOP简介与不同框架设计概览
    • 6.02 AOP设计方案-Middleware
  • PART07.Middleware
    • 7.01 Middleware-AccessLog
    • 7.02 Middleware-Trace简介和OpenTelemetry
    • 7.03 Middleware-OpenTelemetry测试
    • 7.04 Middleware-OpenTelemetry总结
    • 7.05 Prometheus详解
    • 7.06 Middleware-Prometheus
    • 7.07 Middleware-错误页面
    • 7.08 Middleware-从panic中恢复
    • 7.09 Middleware总结和面试
  • PART08.Review
    • 8.01 课后复习-AOP
    • 8.02 课后复习-Context
    • 8.03 课后复习-Middleware-AccessLog
  • PART09.Appendix
    • 附录1.责任链模式
    • 附录2.生成器模式
    • 附录3.函数选项模式
  • xiaochengxu
    • 01.原力去水印
    • 02.KeePass密码管理:安全轻松的管理您的密码
Powered by GitBook
On this page
  • PART1. 基本使用
  • PART2. 结构体echo.Echo
  • PART3. Route和node
  • 3.1 Router
  • 3.2 node
  • PART4. Context
  • PART5. 核心抽象
  • PART6. 框架对比
  • PART7. WEB框架面试题
  • 7.1 Web框架拿来做什么?
  • 7.2 为什么都已经有了http包,还要开发Web框架?
  • 7.3 Web框架的核心?
  1. PART01.Web框架概览

1.05 Web框架概览-Echo框架分析

PART1. 基本使用

package echo

import (
	"github.com/labstack/echo/v4"
	"github.com/labstack/echo/v4/middleware"
	"net/http"
	"testing"
)

func TestHelloWorld(t *testing.T) {
	// Echo instance
	e := echo.New()

	// Middleware
	e.Use(middleware.Logger())
	e.Use(middleware.Recover())

	// Routes
	e.GET("/", hello)

	// Start server
	e.Logger.Fatal(e.Start(":8084"))
}

// Handle
func hello(c echo.Context) error {
	return c.String(http.StatusOK, "Hello, World!")
}

PART2. 结构体echo.Echo

从上述示例的e.Start(":8084")可以看出,echo.Echo结构体(即echo.New()的返回值)类似于Beego的HttpServer和GIN的Engine:

  • 注册路由:暴露了注册路由的方法,但echo.Echo本身并不是路由树的载体(echo.Echo只是组合了echo.Router)

  • 生命周期管理:如Echo.Start()/Echo.Shutdown()等方法

// Echo is the top-level framework instance.
Echo struct {
	filesystem
	common
	startupMutex     sync.RWMutex
	StdLogger        *stdLog.Logger
	colorer          *color.Color
	premiddleware    []MiddlewareFunc
	middleware       []MiddlewareFunc
	maxParam         *int
	router           *Router
	routers          map[string]*Router
	pool             sync.Pool
	Server           *http.Server
	TLSServer        *http.Server
	Listener         net.Listener
	TLSListener      net.Listener
	AutoTLSManager   autocert.Manager
	DisableHTTP2     bool
	Debug            bool
	HideBanner       bool
	HidePort         bool
	HTTPErrorHandler HTTPErrorHandler
	Binder           Binder
	JSONSerializer   JSONSerializer
	Validator        Validator
	Renderer         Renderer
	Logger           Logger
	IPExtractor      IPExtractor
	ListenerNetwork  string
}

可以看到在该结构体中,有2个相似的字段:router和routers:

  • router:表示路由树

  • routers:表示根据Host将路由树隔离,可以看做是类似namespace之类的概念,既是一种组织方式,也是一种隔离机制

一种观点认为:如果想要做一个类似namespace之类的隔离机制,应该使用一些更轻量级的方式.但如果仅仅是为了实现"根据Host将路由树隔离"的功能,不如创建2个Echo实例,而不是使用在1个Echo示例中使用一个map通过key来隔离.

PART3. Route和node

Router:表示路由树,而node表示的是路由树上的节点

3.1 Router

Router struct {
	tree   *node
	routes map[string]*Route
	echo   *Echo
}

3.1.1 Route类型

实际上这里的Route类型才是真正表示1个路由的:

// Route contains a handler and information for matching against requests.
Route struct {
	Method string `json:"method"`
	Path   string `json:"path"`
	Name   string `json:"name"`
}

Router.tree字段表示一棵树的根节点,实际上该字段改名为root更加合适.

3.1.2 Router.echo

从上述echo.Echo结构体的定义中可以看到它有一个字段名为router,其类型为*Router;从Router结构体的定义中也能够看到它有一个字段名为echo,其类型为*Echo.很明显的双向引用.

这也是在有些情况下逼不得已的做法.比如GO原生的sql包中,sql.Tx里边维护了一个sql.DB的实例:

type Tx struct {
	db *DB

	closemu sync.RWMutex

	dc  *driverConn
	txi driver.Tx

	releaseConn func(error)

	done int32

	keepConnOnRollback bool

	stmts struct {
		sync.Mutex
		v []*Stmt
	}

	cancel func()

	ctx context.Context
}

3.2 node

node struct {
	kind           kind
	label          byte
	prefix         string
	parent         *node
	staticChildren children
	originalPath   string
	methods        *routeMethods
	paramChild     *node
	anyChild       *node
	paramsCount    int
	// isLeaf indicates that node does not have child routes
	isLeaf bool
	// isHandler indicates that node has at least one handler registered to it
	isHandler bool

	// notFoundHandler is handler registered with RouteNotFound method and is executed for 404 cases
	notFoundHandler *routeMethod
}

注意字段staticChildren(实际上children类型就是[]*node)、paramChild和anyChild:

  • staticChildren:静态匹配

  • paramChild:参数路径

  • anyChild:通配符匹配

这种设计可以比较容易实现路由优先级和路由冲突检测

PART4. Context

硬要说区别的话,只是在Echo中这个抽象被定义为了一个接口而非结构体.这也就意味着可以为该接口提供不同的实现.当然其实没啥人给它提供不同的实现.

PART5. 核心抽象

  • Route & node:处理路由相关功能

  • Echo:类似Beego中的Beego的HttpServer和GIN中的Engine

  • Context:处理请求上下文

  • HandlerFunc:业务逻辑代码

PART6. 框架对比

Beego
GIN
Iris
Echo

代表服务器

HttpServer

Engine

Application

Echo

代表路由

ControllerRegister

methodTree

Route

Route

上下文

Context

Context

Context

Context

处理逻辑

HandlerFunc

HandlerFunc

HandlerFunc

HandlerFunc

所以实际上我们要造一个Web框架,就是要建立我们自己的这几个抽象.

PART7. WEB框架面试题

实际上,面整体Web框架的还是比较少的.大多数时候,面试都是聊具体的某个Web框架.且如果你打的是一些高端局,也不会问你怎么用,最多最多是问你怎么实现.

7.1 Web框架拿来做什么?

处理HTTP请求,为用户提供便捷API,为用户提供无侵入式的插件机制,提供如上传下载等默认功能.

7.2 为什么都已经有了http包,还要开发Web框架?

  • 高级路由功能

    • 参数匹配

    • 正则匹配

    • 通配符匹配

    • 这些功能shi 原生的net/http包不支持的

  • 封装HTTP上下文以提供简单API

  • 封装Server以提供生命周期控制

  • 设计插件机制以提供无侵入式解决方案

7.3 Web框架的核心?

  • 路由树

  • 上下文Context

  • Server

Last updated 9 months ago

Echo中的是一个大而全的接口,定义了处理请求和响应的各种方法.和Beego、Gin、Iris的Context没有什么区别.

Context
核心抽象总结