代理网站在线做一个网站多少钱
2026/4/14 3:06:30 网站建设 项目流程
代理网站在线,做一个网站多少钱,百度导航如何设置公司地址,wordpress html5刚写Go那会#xff0c;context对我来说就是个到处传的参数#xff0c;函数签名里写上但也不知道有什么用。 后来线上出了几次问题才明白#xff1a;context是Go并发控制的灵魂。 context解决什么问题 想象一个场景#xff1a;用户请求进来#xff0c;你要调…刚写Go那会context对我来说就是个到处传的参数函数签名里写上但也不知道有什么用。后来线上出了几次问题才明白context是Go并发控制的灵魂。context解决什么问题想象一个场景用户请求进来你要调用3个下游服务汇总结果返回。funchandleRequest(w http.ResponseWriter,r*http.Request){result1:callServiceA()result2:callServiceB()result3:callServiceC()response:merge(result1,result2,result3)json.NewEncoder(w).Encode(response)}问题来了如果用户取消请求关闭浏览器下游调用还在跑浪费资源如果ServiceA超时了ServiceB和C还要继续等吗请求的一些上下文信息用户ID、TraceID怎么传下去context就是解决这些问题的。context的四种创建方式1. context.Background()ctx:context.Background()空context通常作为根context在main函数或请求入口使用。2. context.TODO()ctx:context.TODO()也是空context用于还不确定用什么context的场景。语义上表示待定。3. context.WithCancelctx,cancel:context.WithCancel(context.Background())defercancel()// 一定要调用否则会泄漏gofunc(){// 做一些事情select{case-ctx.Done():fmt.Println(被取消了)returncase-time.After(10*time.Second):fmt.Println(完成)}}()// 某个条件触发取消cancel()关键点cancel()必须调用通常defer cancel()取消是传播的父context取消所有子context都取消4. context.WithTimeout / context.WithDeadline// 3秒超时ctx,cancel:context.WithTimeout(context.Background(),3*time.Second)defercancel()// 截止时间deadline:time.Now().Add(3*time.Second)ctx,cancel:context.WithDeadline(context.Background(),deadline)defercancel()区别WithTimeout相对时间从现在开始多久WithDeadline绝对时间到什么时候超时控制实战HTTP请求超时funcfetchURL(ctx context.Context,urlstring)([]byte,error){req,err:http.NewRequestWithContext(ctx,GET,url,nil)iferr!nil{returnnil,err}resp,err:http.DefaultClient.Do(req)iferr!nil{returnnil,err}deferresp.Body.Close()returnio.ReadAll(resp.Body)}// 使用funcmain(){ctx,cancel:context.WithTimeout(context.Background(),5*time.Second)defercancel()data,err:fetchURL(ctx,https://example.com)iferr!nil{iferrors.Is(err,context.DeadlineExceeded){fmt.Println(请求超时)}else{fmt.Println(请求失败:,err)}return}fmt.Println(string(data))}数据库查询超时funcqueryUser(ctx context.Context,db*sql.DB,userIDint)(*User,error){ctx,cancel:context.WithTimeout(ctx,3*time.Second)defercancel()varuser User err:db.QueryRowContext(ctx,SELECT * FROM users WHERE id ?,userID).Scan(user.ID,user.Name,user.Email)iferr!nil{iferrors.Is(err,context.DeadlineExceeded){returnnil,fmt.Errorf(查询超时)}returnnil,err}returnuser,nil}并发请求统一超时funcfetchAll(ctx context.Context,urls[]string)([][]byte,error){ctx,cancel:context.WithTimeout(ctx,10*time.Second)defercancel()results:make([][]byte,len(urls))errs:make([]error,len(urls))varwg sync.WaitGroupfori,url:rangeurls{wg.Add(1)gofunc(iint,urlstring){deferwg.Done()results[i],errs[i]fetchURL(ctx,url)}(i,url)}wg.Wait()// 检查是否超时ifctx.Err()!nil{returnnil,ctx.Err()}// 检查各个请求的错误for_,err:rangeerrs{iferr!nil{returnnil,err}}returnresults,nil}context传值context.WithValuetypecontextKeystringconst(userIDKey contextKeyuserIDtraceIDKey contextKeytraceID)funcWithUserID(ctx context.Context,userIDint)context.Context{returncontext.WithValue(ctx,userIDKey,userID)}funcUserIDFromContext(ctx context.Context)(int,bool){userID,ok:ctx.Value(userIDKey).(int)returnuserID,ok}// 使用funchandleRequest(ctx context.Context){ctxWithUserID(ctx,12345)processOrder(ctx)}funcprocessOrder(ctx context.Context){userID,ok:UserIDFromContext(ctx)if!ok{// 没有userIDreturn}fmt.Println(处理用户,userID,的订单)}最佳实践key用自定义类型避免冲突不要用context传业务数据只传请求范围的元数据TraceID、UserID等提供封装函数不要直接暴露key链路追踪示例typecontextKeystringconsttraceIDKey contextKeytraceIDfuncWithTraceID(ctx context.Context)context.Context{traceID:uuid.New().String()returncontext.WithValue(ctx,traceIDKey,traceID)}funcTraceID(ctx context.Context)string{iftraceID,ok:ctx.Value(traceIDKey).(string);ok{returntraceID}returnunknown}// 日志中间件funcLoggingMiddleware(next http.Handler)http.Handler{returnhttp.HandlerFunc(func(w http.ResponseWriter,r*http.Request){ctx:WithTraceID(r.Context())log.Printf([%s] %s %s 开始,TraceID(ctx),r.Method,r.URL.Path)start:time.Now()next.ServeHTTP(w,r.WithContext(ctx))log.Printf([%s] %s %s 完成 耗时:%v,TraceID(ctx),r.Method,r.URL.Path,time.Since(start))})}// 业务代码中使用funcqueryDB(ctx context.Context){log.Printf([%s] 开始查询数据库,TraceID(ctx))// ...}context在各层的传递一个典型的Web服务// Handler层funcGetUser(w http.ResponseWriter,r*http.Request){ctx:r.Context()userID:chi.URLParam(r,id)user,err:userService.GetUser(ctx,userID)iferr!nil{http.Error(w,err.Error(),500)return}json.NewEncoder(w).Encode(user)}// Service层func(s*UserService)GetUser(ctx context.Context,userIDstring)(*User,error){// 检查缓存user,err:s.cache.Get(ctx,userID)iferrnil{returnuser,nil}// 查数据库user,errs.repo.FindByID(ctx,userID)iferr!nil{returnnil,err}// 写缓存_s.cache.Set(ctx,userID,user)returnuser,nil}// Repository层func(r*UserRepository)FindByID(ctx context.Context,userIDstring)(*User,error){varuser User err:r.db.QueryRowContext(ctx,SELECT id, name, email FROM users WHERE id ?,userID).Scan(user.ID,user.Name,user.Email)returnuser,err}// Cache层func(c*Cache)Get(ctx context.Context,keystring)(*User,error){val,err:c.redis.Get(ctx,key).Result()iferr!nil{returnnil,err}varuser User json.Unmarshal([]byte(val),user)returnuser,nil}规范context作为第一个参数命名为ctx。常见错误1. 忘记cancel// 错误会泄漏funcbad(){ctx,_:context.WithTimeout(context.Background(),time.Second)// ...}// 正确funcgood(){ctx,cancel:context.WithTimeout(context.Background(),time.Second)defercancel()// ...}2. 把context存到struct里// 错误typeServicestruct{ctx context.Context// 不要这样}// 正确context作为方法参数传递typeServicestruct{}func(s*Service)DoSomething(ctx context.Context)error{// ...}context是请求级别的不应该存储。3. 使用context.Value传业务数据// 错误不要用context传业务参数ctxcontext.WithValue(ctx,order,order)funcprocessOrder(ctx context.Context){order:ctx.Value(order).(Order)// ...}// 正确业务数据走参数funcprocessOrder(ctx context.Context,order Order){// ...}4. 不检查context是否取消// 错误长循环不检查ctxfuncprocess(ctx context.Context,items[]Item){for_,item:rangeitems{doHeavyWork(item)// 即使ctx取消了也继续执行}}// 正确funcprocess(ctx context.Context,items[]Item)error{for_,item:rangeitems{select{case-ctx.Done():returnctx.Err()default:}doHeavyWork(item)}returnnil}context的继承关系Background (根) │ ├── WithCancel ────────────┐ │ │ │ cancel() 会取消所有子context │ ├── WithTimeout │ │ │ │ │ │ │ └── WithValue │ │ │ └── WithValue │ └── WithTimeout │ └── WithValue特点取消向下传播父取消子都取消取消不向上传播子取消父不受影响Value沿着链向上查找funcmain(){ctx1:context.Background()ctx2,cancel2:context.WithCancel(ctx1)ctx3,cancel3:context.WithTimeout(ctx2,time.Second)ctx4:context.WithValue(ctx3,key,value)// cancel2() 会同时取消 ctx2, ctx3, ctx4// cancel3() 只取消 ctx3, ctx4// ctx4.Value(key) 能取到// ctx3.Value(key) 取不到Value不向上传播defercancel2()defercancel3()}超时时间的设计经验值// HTTP对外接口总超时constAPITimeout30*time.Second// 单个下游服务调用constServiceTimeout5*time.Second// 数据库查询constDBTimeout3*time.Second// Redis操作constCacheTimeout100*time.Millisecond超时递减funchandleRequest(ctx context.Context){// 请求总超时30sctx,cancel:context.WithTimeout(ctx,30*time.Second)defercancel()// 查缓存100msdata,err:queryCache(ctx)iferrnil{returndata}// 查库3s// 注意这里不要再创建新的timeout context// 因为剩余时间可能不足3s了用父context会自动继承剩余时间data,errqueryDB(ctx)iferr!nil{returnerr}returndata}总结context的三个核心功能取消信号传播cancel()超时控制WithTimeout/WithDeadline请求级数据传递WithValue使用规范context作为第一个参数不要存到struct里cancel必须调用WithValue只传元数据不传业务数据长操作要检查ctx.Done()记住一句话context是请求的生命周期控制器。有问题评论区聊。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询