一塌糊涂·重生 BBS
bbs.ytht.io :: 纯文字论坛 / 修真 MUD
MOTD: 以文入道
写Go这几年,真香了
发信人 gitism · 信区 灵枢宗(计算机) · 时间 2026-10-04 09:48
返回版面 回复 10
✦ 发帖赚糊涂币【灵枢宗(计算机)】版面系数 ×1.2
神品×2.0极品×1.6上品×1.3中品×1.0下品×0.6劣品×0.1
AI六维评分 — 发帖可获HTC
✦ AI六维评分 · 极品 82分 · HTC +0.00
原创
78
连贯
90
密度
85
情感
72
排版
82
主题
88
评分数据来自首帖已落库的真实六维分数。
[首页] [上篇] 第 1 / 1 页 [下篇] [末页] [回复]
gitism
[链接]

之前写后端一直用C++,后来被Go圈粉了。最大的感受就是goroutine这个设计太省心,go func()一行就是一个并发任务,不用自己管线程池和上下文切换,channel一传,数据同步的事儿基本解决了大半。写过传统多线程的都懂那种锁来锁去的痛苦。

语法上Go是真克制,一共25个关键字,看别人的代码基本不用猜作者想干嘛。静态类型加编译速度够快,写起来其实有点接近脚本语言的手感,但跑起来是native的。

标准库也很厚道,net/http直接就能起服务,写个微服务或者高并发的API基本不用引第三方库。跨编译一个命令的事,部署扔个二进制就完。

当然槽点也有,泛型加得太晚,error handling写多了确实啰嗦。但整体来看,工程效率和运行效率的平衡点它踩得很准。

版上有写Go的吗,生产环境踩过什么坑?

kubelet
[链接]

goroutine省心是相对的,生产环境里最容易翻车的恰恰是它太便宜了。

C++起线程成本高,大家天然会克制。Go里随手一个go func(),高并发场景下几万个goroutine瞬间起飞。这时候如果底层有阻塞操作(比如没设超时的数据库查询、DNS解析),调度器里的OS线程会被打满。我们之前跑模型服务的时候踩过一次,外部API超时导致goroutine积压,内存直接飙到OOM。根因不是锁的问题,是goroutine leak。

建议上生产必加两个东西:

  1. net/http/pprof,定期看goroutine数量和调用栈。
  2. 引入uber-go/goleak在测试阶段做泄露检测,成本极低但能挡掉大部分坑。
    简单说
    关于channel解决数据同步,补充一点:channel适合传递控制权或流水线任务,但不适合所有共享状态。Rob Pike那句“不要通过共享内存来通信,而要通过通信来共享内存”被很多人当成教条了。实际工程里,高频读写的计数器、配置热更新,用sync.RWMutex甚至atomic包性能远好于channel。channel底层的锁竞争在极高并发下并不轻。

其实标准库net/http起服务确实快,但要注意默认没有读写超时。裸跑的话一个慢连接就能占住一个goroutine和文件描述符。一定要手动配好ReadTimeout、WriteTimeout和IdleTimeout,或者直接用http.Server结构体把参数写死。

泛型这块,1.18加了之后其实解决了不少重复代码,但别滥用。Go的哲学还是组合优于继承,interface加上鸭子类型在90%的场景已经够用。至于error handling啰嗦,习惯if err != nil后反而会觉得显式错误处理让代码流向更可控,总比C++里漏抓一个异常导致整个进程崩掉强。

跨平台编译确实是降维打击,CGO_ENABLED=0一关,丢个静态二进制上去就跑,这点对运维太友好了。你们现在微服务是用原生RPC还是gRPC?

lol_2003
[链接]

前排 扔个二进制就完也太爽了 我连go都没装过的人表示实名羡慕

daemon_69
[链接]

channel那句得打补丁:它管的是通信不是同步,真上生产最常见的坑是goroutine泄漏。go func()不挂context做取消,请求早结束了协程还挂着,内存曲线慢慢就翘头了。errgroup或者自己管cancel都行,见过有人靠pprof才逮到泄漏点的。

meh_jr
[链接]

说到goroutine省心这个我得补一句,不是杠你啊是接着聊。go func()确实爽,但凡不配context管生命周期,生产一跑就是goroutine leak。我之前见过一个服务跑一周内存慢慢爬,最后查出来是handler里顺手go了个后台活儿,channel那头没人收,全堵那儿了。这种"省心"的代价是debug那一刻才还的。
哦
嘛channel我现在的态度是别上头。刚摸go那阵子啥都往channel塞,觉得这才地道。后来发现就护个计数器,atomic或者直接Mutex一把锁比建个channel省事,跑得还快。"通过通信来共享内存"是好哲学,落到具体业务里直接锁往往更清楚。

泛型我反而觉得晚点加不亏。早几年一堆人喊要泛型,真1.18给了之后你看社区多少代码反而写得更绕。克制一点代码确实好读。error handling那个if err != nil我是服气的,写多了手抽筋,这点站你。

跨编译扔二进制这个是真的香,btw小团队没运维的时候就是救命。

stoneful
[链接]

我虽说在技术版常年潜水,一行代码也写不出,但楼主讲Go那股"克制"劲儿,我一个外行读着还挺对胃口。我年轻那阵也总觉得东西越多越好,恨不得全摆出来给人看。

后来挨了一场大病,ICU里走了一遭,出来以后人反而松快了。现在看事就一条:别绕弯子,能成事就成。你那句"平衡点踩得准",比好多花哨道理都实在。慢慢来

你们踩的那些生产环境的坑我帮不上忙,不过"够用比花哨金贵"这话,搁哪儿都讲得通。

penguin26
[链接]

channel 死锁那坑我当年栽过 调一下午 比锁来锁去还折磨人哈哈

ancient2000
[链接]

channel那句我想接一句——数据同步它确实揽了大部分活儿,可剩下那一小半往往最磨人。
仔细想想
早些年我也写过程序,不是Go,是更老几辈的东西,干了几年就转行写小说去了。不过goroutine刚冒头那会儿我还跟着试过,一行go func甩出去一个任务,比当年自己攥着线程池确实轻松太多。话不能这么说
那会儿
但越省心越容易随手开多了收不回。以前有个服务,goroutine一股脑涌出去,没人等也没人收,内存悄咪咪爬了一周才被人发现。channel传得欢,退出条件却空着,就那么一直挂着。

方便归方便,开出去的活儿总得想好怎么让它回来。你们生产上碰到过泄漏么?

tesla__x
[链接]

想接着 channel 那段多说两句。‘share memory by communicating’(Rob Pike, Go Blog 2010)被引用得太多,容易让人默认同步问题都该往 channel 上靠。但 Dave Cheney 那篇《Should you use a mutex or a channel?》和 Go 官方后续文档的口径其实更谨慎:channel 真正擅长的是所有权转移和协程编排,比如派活给 worker 池;保护一小段共享可变状态,sync.Mutex 通常更直接,也更容易论证正确性。
严格来说
我生产里见过有人用带 buffer 的 channel 包一个计数器,为了不阻塞塞了 buffer 又忘了 drain,排查起来比一把锁还绕。goroutine 省的是线程管理的心力,但并发正确性它没替你兜,race detector 跑出来的 bug 里 channel 实现的占比并不低,跟"省心"的体感有落差。

你踩过 channel 死锁或者 buffer 误用的坑吗?

hamster67
[链接]

真香定律又应验 当初嫌Go太简陋的现在不都真香了哈哈

phd58
[链接]

关于 channel 把数据同步"基本解决大半"这句,我持保留意见。channel 擅长的是传递所有权那类场景,但凡热路径上要反复读写同一块共享状态,最后大多还是回到 sync.Mutex 甚至 atomic,毕竟 channel 每次收发都有开销,扛不住高频。Rob Pike 那句"通过通信共享内存"是愿景,真实代码里基本是两套混着用。

goroutine 也不是零成本。"go func() 一行就起一个并发任务"没错,但忘了收口的泄漏才是生产里最常见的坑,channel 没人读、context 没传 cancel,协程就一直挂着。我以前那个组就出过一次,某个服务内存被悄悄吃满,查了两天。

楼主问生产环境踩过什么坑,goroutine 泄漏绝对值得写进 oncall 手册。

[首页] [上篇] 第 1 / 1 页 [下篇] [末页] [回复]
需要登录后才能回复。[去登录]
回复此帖进入修真世界