pupuk / blog

My New Blog. Record & Share. Focus on PHP, MySQL, Javascript and Golang.
MIT License
9 stars 2 forks source link

Go Fiber 尝试的一些问题和思考 #35

Open pupuk opened 2 years ago

pupuk commented 2 years ago

目前主流的go web frame有 Gin Echo Iris beego等,现状是Gin各方面平衡的很好,最为主流。https://github.com/gin-gonic/gin 2020年起,Fiber发展很好,大有挑战Gin的意思。 Gin VS FIber Gin 使用的是net/http,见:https://github.com/gin-gonic/gin/blob/master/gin.go#L11 Fiber使用的是fasthttp,见:https://github.com/gofiber/fiber/blob/master/app.go#L33 从理论上来说Fiber的并发性能会更优。

下面记录一些在使用研究中的一些思考:

1、c.Param()不能获取URL的%##编码前的内容

image image image 其实不能怪Fiber,因为浏览器先将“蒲” 转成了 “%E8%92%B2”,再发起http请求到后端的。 但是Gin默认都会将URL解码出来,Fiber没有。

经查找发现: Fiber New的时候,可以配置参数,将:UnescapePath 设置成true即可。fiber的默认配置是false,我有点不理解。对于非英语国家的fiber初学者,很容易因为这个被劝退

app := fiber.New(fiber.Config{
    UnescapePath: true,
})

image

更多配置参见文档: https://docs.gofiber.io/api/fiber 建议还是先过一眼,免得遇到问题,没有思路,还以为是框架的问题。

pupuk commented 2 years ago

2、Fiber 使用Recover中间件的方法

初始化Gin有两种方法 gin.New() https://github.com/gin-gonic/gin/blob/master/gin.go#L163 gin.Default() https://github.com/gin-gonic/gin/blob/master/gin.go#L195 看源码可看出:Default()多使用了 Logger和Recovery中间件

// Default returns an Engine instance with the Logger and Recovery middleware already attached.
func Default() *Engine {
    debugPrintWARNINGDefault()
    engine := New()
    engine.Use(Logger(), Recovery())
    return engine
}

对于Web应用来说,任意一个goroutine panic导致整个程序退出的话,是很严重的问题。而很多panic在编译期间并不能发现,比如a/b, b的值如果是从数据库取的,且等于零,就会panic。 所以对于panic使用recover来捕获,至关重要。 Fiber默认没有启用recover中间件,我也挺吃惊的。若果让新手认为程序不稳定,再劝退一些人。对于Fiber的发展并不好。唯一欣慰的是recovery不再作为第三方中间件了,是官方自带的中间件。 一个使用recover中间件的例子:

package main

import (
    "github.com/gofiber/fiber/v2"
    "github.com/gofiber/fiber/v2/middleware/recover"
)

func main() {
    app := fiber.New(fiber.Config{
        UnescapePath: true,
    })
    app.Use(recover.New())

    app.Get("/name/:family", func(c *fiber.Ctx) error {
        return c.SendString(c.Params("family"))
    })

    app.Get("/panic", func(c *fiber.Ctx) error {
        panic("panic...")
    })

    app.Listen(":3000")
}

Recover中间件的原理:

go自带的recover()就可以捕获panic,配合defer使用,可以防止程序异常退出,简单demo见:https://gobyexample.com/recover

Fiber的recover中间件代码也确实简单,原理一样,只是区分了对日志的处理: https://github.com/gofiber/fiber/blob/master/middleware/recover/recover.go#L30

pupuk commented 2 years ago

3、熟悉context的各种方法

https://github.com/gofiber/fiber/blob/master/ctx.go 这个文件中实现了context,http请求和响应的大部分功能,熟悉c.XXX() ,有助于熟悉理解框架。这里用正则提取,记录一下。 grep -P 'func \(c \*Ctx\).*\{' ctx.go 结果:

func (c *Ctx) Accepts(offers ...string) string {
func (c *Ctx) AcceptsCharsets(offers ...string) string {
func (c *Ctx) AcceptsEncodings(offers ...string) string {
func (c *Ctx) AcceptsLanguages(offers ...string) string {
func (c *Ctx) App() *App {
func (c *Ctx) Append(field string, values ...string) {
func (c *Ctx) Attachment(filename ...string) {
func (c *Ctx) BaseURL() string {
func (c *Ctx) Body() []byte {
func (c *Ctx) BodyParser(out interface{}) error {
func (c *Ctx) ClearCookie(key ...string) {
func (c *Ctx) Context() *fasthttp.RequestCtx {
func (c *Ctx) UserContext() context.Context {
func (c *Ctx) SetUserContext(ctx context.Context) {
func (c *Ctx) Cookie(cookie *Cookie) {
func (c *Ctx) Cookies(key string, defaultValue ...string) string {
func (c *Ctx) Download(file string, filename ...string) error {
func (c *Ctx) Request() *fasthttp.Request {
func (c *Ctx) Response() *fasthttp.Response {
func (c *Ctx) Format(body interface{}) error {
func (c *Ctx) FormFile(key string) (*multipart.FileHeader, error) {
func (c *Ctx) FormValue(key string, defaultValue ...string) string {
func (c *Ctx) Fresh() bool {
func (c *Ctx) Get(key string, defaultValue ...string) string {
func (c *Ctx) GetRespHeader(key string, defaultValue ...string) string {
func (c *Ctx) Hostname() string {
func (c *Ctx) Port() string {
func (c *Ctx) IP() string {
func (c *Ctx) IPs() (ips []string) {
func (c *Ctx) Is(extension string) bool {
func (c *Ctx) JSON(data interface{}) error {
func (c *Ctx) JSONP(data interface{}, callback ...string) error {
func (c *Ctx) Links(link ...string) {
func (c *Ctx) Locals(key string, value ...interface{}) (val interface{}) {
func (c *Ctx) Location(path string) {
func (c *Ctx) Method(override ...string) string {
func (c *Ctx) MultipartForm() (*multipart.Form, error) {
func (c *Ctx) Next() (err error) {
func (c *Ctx) OriginalURL() string {
func (c *Ctx) Params(key string, defaultValue ...string) string {
func (c *Ctx) ParamsInt(key string, defaultValue ...int) (int, error) {
func (c *Ctx) Path(override ...string) string {
func (c *Ctx) Protocol() string {
func (c *Ctx) Query(key string, defaultValue ...string) string {
func (c *Ctx) QueryParser(out interface{}) error {
func (c *Ctx) Range(size int) (rangeData Range, err error) {
func (c *Ctx) Redirect(location string, status ...int) error {
func (c *Ctx) Render(name string, bind interface{}, layouts ...string) error {
func (c *Ctx) Route() *Route {
func (c *Ctx) SaveFile(fileheader *multipart.FileHeader, path string) error {
func (c *Ctx) Secure() bool {
func (c *Ctx) Send(body []byte) error {
func (c *Ctx) SendFile(file string, compress ...bool) error {
func (c *Ctx) SendStatus(status int) error {
func (c *Ctx) SendString(body string) error {
func (c *Ctx) SendStream(stream io.Reader, size ...int) error {
func (c *Ctx) Set(key string, val string) {
func (c *Ctx) setCanonical(key string, val string) {
func (c *Ctx) Subdomains(offset ...int) []string {
func (c *Ctx) Stale() bool {
func (c *Ctx) Status(status int) *Ctx {
func (c *Ctx) String() string {
func (c *Ctx) Type(extension string, charset ...string) *Ctx {
func (c *Ctx) Vary(fields ...string) {
func (c *Ctx) Write(p []byte) (int, error) {
func (c *Ctx) WriteString(s string) (int, error) {
func (c *Ctx) XHR() bool {
func (c *Ctx) configDependentPaths() {
func (c *Ctx) IsProxyTrusted() bool {
pupuk commented 2 years ago

4、熟悉App的各种方法,用类似的正则提取

https://raw.githubusercontent.com/gofiber/fiber/master/app.go 中的app方法。fiber框架的主要功能也就在 app.go和ctxt.go两个文件中

func (app *App) handleTrustedProxy(ipAddress string) {}
func (app *App) Mount(prefix string, fiber *App) Router {}
func (app *App) Name(name string) Router {}
func (app *App) GetRoute(name string) Route {}
func (app *App) Use(args ...interface{}) Router {}
func (app *App) Get(path string, handlers ...Handler) Router {}
func (app *App) Head(path string, handlers ...Handler) Router {}
func (app *App) Post(path string, handlers ...Handler) Router {}
func (app *App) Put(path string, handlers ...Handler) Router {}
func (app *App) Delete(path string, handlers ...Handler) Router {}
func (app *App) Connect(path string, handlers ...Handler) Router {}
func (app *App) Options(path string, handlers ...Handler) Router {}
func (app *App) Trace(path string, handlers ...Handler) Router {}
func (app *App) Patch(path string, handlers ...Handler) Router {}
func (app *App) Add(method, path string, handlers ...Handler) Router {}
func (app *App) Static(prefix, root string, config ...Static) Router {}
func (app *App) All(path string, handlers ...Handler) Router {}
func (app *App) Group(prefix string, handlers ...Handler) Router {}
func (app *App) Route(prefix string, fn func(router Router), name ...string) Router {}
func (app *App) Listener(ln net.Listener) error {}
func (app *App) Listen(addr string) error {}
func (app *App) ListenTLS(addr, certFile, keyFile string) error {}
func (app *App) ListenMutualTLS(addr, certFile, keyFile, clientCertFile string) error {}
func (app *App) Config() Config {}
func (app *App) Handler() fasthttp.RequestHandler {}
func (app *App) Stack() [][]*Route {}
func (app *App) HandlersCount() uint32 {}
func (app *App) Shutdown() error {}
func (app *App) Server() *fasthttp.Server {}
func (app *App) Hooks() *hooks {}
func (app *App) Test(req *http.Request, msTimeout ...int) (resp *http.Response, err error) {}
func (app *App) init() *App {}
func (app *App) ErrorHandler(ctx *Ctx, err error) error {}
func (app *App) serverErrorHandler(fctx *fasthttp.RequestCtx, err error) {}
func (app *App) startupProcess() *App {}
func (app *App) startupMessage(addr string, tls bool, pids string) {}
func (app *App) printRoutesMessage() {}