苦学golang半年,写了一款web服务器
文章目录
项目地址:https://github.com/fengyuan-liang/jet-web-fasthttp
可以的话,请star支持一下🙂
苦学golang半年,写了一款web服务器,里面包含笔者各种工程实践,大佬勿喷😊
为什么不使用
Gin
,而要自己开发一款web服务器呢?其实gin已经非常好了👍,笔者这里主要是想要把自己开发中的工程实践提炼出来,打造出更加顺手的兵器🏹️(现在还只是个玩具🪀,大家看个乐子就行)
那么在使用
Gin
中有哪些痛点呢?
- 繁琐的路由表,在
Gin
中必须写路由表来映射路由,再写对应的func(ctx *gin.Context )
,总感觉多写了一次,而且接口一多,看着一望无际的路由表,总感觉划分的不是很优雅,而且不太好找自己想要的接口;并且笔者在写springBoot项目时,非常喜欢用restful
插件来找路由,比如输入/v1/xxx/xxx/xxx
就能找到对应的路由,但是在gin的路由表不是很好找 - 每个gin的路由都必须要手动获取参数,然后校验,校验不通过再返回错误(像下面那样),这部分逻辑感觉完全应该复用(完全忍受不了写重复代码😠)。参数就应该交给框架解析,或者说有
切面
或者hook
来统一完成这部分的逻辑// ginengine.Get("/v1/xxx", xxx)funcxxx(ctx *gin.Context){var( err error params xxxx )// Bind your param dataif err =ShouldBindQuery(ctx,¶ms); err !=nil{ ctx.AbortWithStatusJSON(http.StatusOK,"traceId",400,"bad request"))return}// validator your paramif err = validator.New().Struct(¶ms);err !=nil{ ctx.AbortWithStatusJSON(http.StatusOK,"traceId",400,"bad request"))return}// do you code ctx.JSON(http.StatusOK,"traceId","ok"))}// 相比之下,参数Jet会自动帮你注入到你的参数列表里面,并且可以定义Hook统一在参数解析完毕,调用我们自己方法之前处理参数校验的逻辑// Jetfunc(YourJetController)GetV1Xxx(ctx jet.Ctx, args *Xxx)(any,error){// do you codereturn xxx, err}
- 接下来不是
Gin
的缺点,毕竟Gin
只是一个基础的web框架,就是笔者更喜欢MVC
架构或者DDD
模式开发,这里面使用到依赖注入管理生命周期是比较合适的,笔者也不喜欢用类似wire
需要生成代码的方式进行依赖注入,所有笔者使用Dig
进行依赖注入,反射的方式其实也只影响项目启动的时间,但是go的启动本身就很快了,看不出啥影响 - 然后就是定义了一些常用的数据结构,例如
Trie
、LinkedHashMap
,在golang里面其实提供的数据结构挺少的,但是像LinkedHashMap
用的地方其实很多,我们需要O(1)
级别的查找和添加,又需要有序的集合顺序funcTestLinkedHashMap(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 TestLinkedHashMapone:1two:2three:3PASSok GoKit/collection/maps 0.166s
example
下面是使用的一个例子
funcmain(){//jet.Register(&DemoController{})
xlog.SetOutputLevel(xlog.Ldebug)
jet.AddMiddleware(jet.TraceJetMiddleware)
jet.Run(":8080")}// 使用依赖注入的方式注入需要让Jet管理的Controllerfuncinit(){
jet.Provide(NewDemoController)}funcNewDemoController() 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))}returnnil}// PostMethodExecuteHook restful 将所有请求以restful方式返回func(BaseController)PostMethodExecuteHook(param any)(data any, err error){// restfulreturn 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)returnmap[string]any{"request_id": ctx.Logger().GenReqId(),"code":200, data: args}}
$ curl http://localhost:8080/v1/usage/1/week
{"request_id":"H5OQ4Jg0yBtg","code":200,"message":"success","data":["1"]}
正常情况下会打印日志的全路径,我们在启动时候加上
-trimpath
就可以只打印项目的path
版权归原作者 每天都要加油呀! 所有, 如有侵权,请联系我们删除。