苦学golang半年,写了一款web服务器

苦学golang半年,写了一款web服务器

文章目录

项目地址:https://github.com/fengyuan-liang/jet-web-fasthttp

苦学golang半年,写了一款web服务器,里面包含笔者各种工程实践,大佬勿喷😊

为什么不使用Gin,而要自己开发一款web服务器呢?其实gin已经非常好了👍,笔者这里主要是想要把自己开发中的工程实践提炼出来,打造出更加顺手的兵器🏹️(现在还只是个玩具🪀,大家看个乐子就行)

那么在使用Gin中有哪些痛点呢?

  • 繁琐的路由表,在Gin中必须写路由表来映射路由,再写对应的func(ctx *gin.Context ),总感觉多写了一次,而且接口一多,看着一望无际的路由表,总感觉划分的不是很优雅,而且不太好找自己想要的接口;并且笔者在写springBoot项目时,非常喜欢用restful插件来找路由,比如输入/v1/xxx/xxx/xxx就能找到对应的路由,但是在gin的路由表不是很好找

  • 每个gin的路由都必须要手动获取参数,然后校验,校验不通过再返回错误(像下面那样),这部分逻辑感觉完全应该复用(完全忍受不了写重复代码 😠)。参数就应该交给框架解析,或者说有切面或者hook来统一完成这部分的逻辑

    go 复制代码
    // gin
    engine.Get("/v1/xxx", xxx)
    
    func xxx(ctx *gin.Context) {
      var (
        err error
        params xxxx
      )
      // Bind your param data
    	if err = ShouldBindQuery(ctx, &params); err != nil {
        ctx.AbortWithStatusJSON(http.StatusOK, "traceId", 400, "bad request"))
    		return
    	}
      // validator your param
      if err = validator.New().Struct(&params) ;err != nil {
    		ctx.AbortWithStatusJSON(http.StatusOK, "traceId", 400, "bad request"))
    		return
    	}
      // do you code
    	ctx.JSON(http.StatusOK, "traceId", "ok"))
    }
    // 相比之下,参数Jet会自动帮你注入到你的参数列表里面,并且可以定义Hook统一在参数解析完毕,调用我们自己方法之前处理参数校验的逻辑
    // Jet
    func(YourJetController) GetV1Xxx(ctx jet.Ctx, args *Xxx) (any, error) {
      // do you code
      return xxx, err
    }
  • 接下来不是Gin的缺点,毕竟Gin只是一个基础的web框架,就是笔者更喜欢MVC架构或者DDD模式开发,这里面使用到依赖注入管理生命周期是比较合适的,笔者也不喜欢用类似wire需要生成代码的方式进行依赖注入,所有笔者使用Dig进行依赖注入,反射的方式其实也只影响项目启动的时间,但是go的启动本身就很快了,看不出啥影响

  • 然后就是定义了一些常用的数据结构,例如TrieLinkedHashMap,在golang里面其实提供的数据结构挺少的,但是像LinkedHashMap用的地方其实很多,我们需要O(1)级别的查找和添加,又需要有序的集合顺序

    go 复制代码
    func TestLinkedHashMap(t *testing.T) {
        m := maps.NewLinkedHashMap[string, int]()
    
        m.Put("one", 1)
        m.Put("two", 2)
        m.Put("three", 3)
    
        m.ForEach(func(k string, v int) {
            t.Logf("%s: %d\n", k, v)
        })
    }
    
    $ go test -run TestLinkedHashMap
    one: 1
    two: 2
    three: 3
    PASS
    ok      GoKit/collection/maps   0.166s

example

下面是使用的一个例子

go 复制代码
func main() {
	//jet.Register(&DemoController{})
	xlog.SetOutputLevel(xlog.Ldebug)
	jet.AddMiddleware(jet.TraceJetMiddleware)
	jet.Run(":8080")
}

// 使用依赖注入的方式注入需要让Jet管理的Controller
func init() {
	jet.Provide(NewDemoController)
}

func NewDemoController() jet.ControllerResult {
	return jet.NewJetController(&DemoController{})
}

type BaseController struct {
	jet.IJetController
}

// 对参数进行校验,如果不通过会返回`reg_err_info`中定义的错误
func (BaseController) PostParamsParseHook(param any) error {
	if err := utils.Struct(param); err != nil {
		return errors.New(utils.ProcessErr(param, err))
	}
	return nil
}

// PostMethodExecuteHook restful 将所有请求以restful方式返回
func (BaseController) PostMethodExecuteHook(param any) (data any, err error) {
	// restful
	return utils.ObjToJsonStr(param), nil
}

// 上面的两个hook可以直接让controller继承jet.BaseJetController,这样就不用写了

type DemoController struct {
	BaseController
}

type Person struct {
	Name string `json:"name" validate:"required" reg_err_info="不能为空"` // 校验不通过会返回`reg_err_info`的内容
	Age  int    `json:"age"`
}

// 路由 get /v1/usage/{id}/week 已经可以访问了
func (j *DemoController) GetV1Usage0Week(ctx jet.Ctx, args *jet.Args) (any, error) {
	ctx.Logger().Infof("GetV1Usage0Week %v", *args)
  return map[string]any{"request_id": ctx.Logger().GenReqId(), "code": 200, data: args}
}
go 复制代码
$ curl http://localhost:8080/v1/usage/1/week
{"request_id":"H5OQ4Jg0yBtg","code":200,"message":"success","data":["1"]}

正常情况下会打印日志的全路径,我们在启动时候加上-trimpath就可以只打印项目的path

相关推荐
cwj&xyp6 分钟前
Python(二)str、list、tuple、dict、set
前端·python·算法
dlnu20152506228 分钟前
ssr实现方案
前端·javascript·ssr
古木201912 分钟前
前端面试宝典
前端·面试·职场和发展
正在走向自律1 小时前
阿里云ESC服务器一次性全部迁移到另一个ESC
服务器·阿里云·云计算
gywl2 小时前
openEuler VM虚拟机操作(期末考试)
linux·服务器·网络·windows·http·centos
轻口味2 小时前
命名空间与模块化概述
开发语言·前端·javascript
前端小小王2 小时前
React Hooks
前端·javascript·react.js
迷途小码农零零发2 小时前
react中使用ResizeObserver来观察元素的size变化
前端·javascript·react.js
了一li3 小时前
Qt中的QProcess与Boost.Interprocess:实现多进程编程
服务器·数据库·qt