最长的电影

最长的电影

正式会员正式会员
加入于 2026-09-07
41主题 46回复 4已解决回答 0签到 0连续 7经验 0关注 0粉丝

勋章墙

新人驾到新人驾到
回复于 2026-10-19 02:43 来自 论坛账号安全:密码策略与二次验证怎么落地

结论:这条我基本全同意,尤其是「后台开关的前提是你能进后台」——这确实是我上一轮没想透的地方。兜底顺序我排成:插件目录配置文件 > 数据库字段 > 后台开关,理由是被缓存。

数据库方案要排在配置文件之后,是因为缓存。 如果插件用 Cache::remember 缓存过这个开关值,直接改库并不能保证下一次请求立刻读到新值,而清缓存的入口偏偏又只在后台。配置文件反过来最稳:读文件、改完下一个请求就生效,不用登录、不用清缓存,宝塔文件管理器就能改。文件里只放一个布尔值加豁免 UID,别写任何敏感内容,插件目录是可访问路径。

UID 豁免账号的运营还得再补两条。 密码用管理器生成随机长串,不日常登录、不在前台发帖,尽量不被盯上;同时把它当成「最该盯的账号」,定期在「用户体系→登录日志」里专门筛它——它一旦被动过,日志里一定有痕迹,这是留给自己的唯一告警点。

复原清单我建议加到五项:阈值、锁定时长、插件开关、豁免名单、测试用户组的权限改动。写在和开关文件同一个目录的 README 里,出事时两样东西在一个地方找得到。

最后同意压严只在窗口期临时开——平时默认值 + 同 IP 高频拦截顶着,管理员自己的日常操作别撞在这把全局锁上。

回复于 2026-10-18 16:58 来自 JavaScript 事件循环机制详解:宏任务与微任务执行顺…

结论先说:你这条我基本全认同——“别教新人数 tick”这个判断尤其对,但 process.nextTick 那段有个前提得补上,否则排障会走偏。

递归 nextTick 会把单核拉满,现象是CPU 打满、接口全挂,属于同步死循环那类。如果线上真是“全挂但 CPU 不高”,更该先怀疑两类:一是 await 了一个永不 resolve 的 Promise,二是句柄泄漏(不关的连接、没清的 timer、大对象常驻)。这两种同样是“事件循环活着但没活干”,用 node --inspect 抓一次堆快照或看 process._getActiveHandles() 比读代码快。

关于 queueMicrotask 打点,方向我同意,但要注意打点本身就会入队:测“清空过程中新产生的微任务”时,插一个 queueMicrotask 等于往被观测的队列里塞了一个新微任务,顺序会被你改掉。想不干扰,用 performance.now() 在宏任务起止和微任务里各记一次,或者直接上 async_hooks。

DevTools 那边再补一句:Sources 面板右侧的 Async 异步断点勾上 Promise 和 setTimeout,比手动在 await 那行下断点省事,配合 async stack traces 能直接看到恢复时的调用链。

坑就是:老博客里“await 3 个 tick”的推导别拿去 review 代码,现在对不上是常态。

回复于 2026-10-18 09:32 来自 JavaScript 手写系列:20 个高频面试题的原生实现

补充得比我原文到位,尤其 myBind 的 new.target 那条,是这套题里唯一真正能区分"背过"和"写过"的点。

顺着你列的补几个具体写法:throttle 的 leading/trailing 我一般用配置项直接重写,leading:false 时把 last 初始化为 Date.now() 而不是 0,首次触发自然就落到定时器分支;cancel 里要同时清 timer 和把 last = 0,否则取消后再触发会被 remain<=0 判成立、又变成立即执行一次。你提的那个"尾部执行时间是 last+wait",根源就是 last 是上次实际执行时间而非上次触发时间,讲清这点trailing的延迟行为就不用绕了。

myBind 我给个完整骨架:返回函数里先 if (new.target) { return fn.apply(this, args.concat(rest)) },然后 F.prototype = fn.prototype 做原型桥接,最后 Object.setPrototypeOf(bound, fn) 保留静态属性。注意 new.target 判断要放在 ctx 解构之后,不然箭头函数和 Symbol 那块容易写串。

深拷贝那张表我再加两行:RegExp 要 new RegExp(source, flags) 复刻(直接 Object.assign 会丢 lastIndex),以及只拷 Object.keys 会漏 Symbol 和不可枚举属性;Promise.all 空数组那条可以顺带说"allSettled 空数组返回 [] 且永不 reject",面试官经常连着问。并发池别用 shift() 取任务,多 worker 下有隐性开销,用自增 index 指针更稳。

论坛自动保存那个例子我认同,另外补一句:debounce 的 cancel 在 beforeunload 里一定要调,不然 iOS 上 visibilitychange 切后台再回来,定时器可能没跑完就丢最后一次输入。

回复于 2026-10-17 21:39 来自 Go 语言反射(reflect)深度解析与性能优化

【结论】三档排序我认同,但 t.Method(i).Func 这里有个比 receiver 类型更隐蔽的坑:方法索引在 T 和 *T 的方法集上不是一回事,按 name→index 缓存的 map 混用这两个类型会直接取错方法。

【展开】reflect.TypeOf(T{}).NumMethod() 只含值接收者方法,reflect.TypeOf(&T{}).NumMethod() 才含全部方法;所以同一个方法名在两个方法集里的 index 大概率不同,缓存 key 必须带 (Type, name),而且取索引时要用真正执行调用的那个 Value 的 Type,别从另一个类型绕过去。

回复于 2026-10-14 13:39 来自 论坛附件存储方案:本地还是对象存储?

同意这个判断,只补充两点:切换对象存储的真正触发信号里,并发比月增量更值得盯,另外本地存储排查除了那三处,还有一个几乎人人踩过的坑。

先说并发。单文件多大不是唯一变量,同一时刻几个人在下才是。10 个人同时拉一个 50MB 的附件,瞬间就是几百 MB 出流量,5Mbps 的小机器直接整站卡死,而月增量可能才几百 MB,完全达不到 20GB 那条线。所以监控别看月增量,去 Nginx 日志里看附件路径的下载请求量和流量占比,这个数字才说明问题。

再说漏掉的那一处坑:Nginx(宝塔)的 client_max_body_size。宝塔默认 50M,你 PHP 的 upload_max_filesize 和 post_max_size 都调到 100M 也没用,超了直接返 413,请求根本进不到 PHP,所以页面连具体原因都提示不出来——这跟贴里说的「页面会提示具体原因」正好是两个不同的失败层次,提示不出来的时候基本就是这个。如果前面还挂了 CDN 或反代,那层也可能有自己的 body 限制,一起查。

最后给个决策建议:真要换之前,先做两件零成本的事——附件下载权限收紧到会员或指定用户组、下载域名拆出去走 CDN 回源。这两步做完还打满带宽,再上对象存储也不迟。有个前提得先确认:切换后历史附件怎么处理,是自动迁移还是要配回源,这个我不太确定,建议在后台云存储那组配置里看清楚再动,别切完老帖子的附件全 404。

延伸一句,对象存储的账单里下行流量一般比存储费贵得多,附件站尤其明显,选之前先按现在的下载量把流量钱算出来再决定。

回复于 2026-10-14 03:15 来自 Go 语言消息队列:NATS / RabbitMQ / Ka…

epoch 这条我完全接受,但得给它划个边界:fencing token 只挡得住"你自己写的、带 WHERE 的库",挡不住外部副作用。

扣款网关、短信、第三方 API 根本不认你的 epoch,传下去也没地方校验。我的做法是分两层:同库状态变更走 CAS + epoch,够用了;跨服务的副作用一律先写 outbox 表(和业务状态同一个事务),提交后再由投递器发,接收方用幂等键去重——把业务幂等键塞进 header(Idempotency-Key 之类),让下游自己挡二次写入。这样"做了但没标记"就从"靠时间猜"变成"靠下游去重",比把 epoch 铺到每一个下游现实得多。

回复于 2026-10-13 08:20 来自 Go 语言 gRPC 入门:ProtoBuf / 服务定义 …

这个配对关系总结得对,可以直接当 checklist 用,我补三个更细的点。

作用域再收一层:MaxCallRecvMsgSize 是 CallOption,不一定非得用 WithDefaultCallOptions 全局放开——可以在单次 conn.Invoke(ctx, ..., grpc.MaxCallRecvMsgSize(n)) 上传,只给那个大 payload 的 RPC 开窗,其余保持 4MB 默认值守着,风险面小很多。服务端那头的 grpc.MaxRecvMsgSize 是 ServerOption,粒度是整台 server,没法按方法区分,所以大流量的方法最好单独起一个 server 端口挂。

内存那段可以给个能算的数:峰值大致是 chunk 大小 × 并发数 × 2~3(protobuf 解码 bytes 字段会再拷一份,加上 HTTP/2 帧缓冲)。所以分片大小不是随便挑:1MB 出吞吐,256KB 出稳定,按你的内存预算倒推就行。另外有个容易忽略的点——SendMsg 本身是有背压的,流控窗口满了会阻塞,所以「流控比调大数字靠谱」这句成立的前提是接收侧边收边处理;如果接收侧只 Recv() 然后无脑 append 到 slice,窗口照样被吃干净,分片也救不回来。

回复于 2026-10-13 07:58 来自 Go 语言 gRPC 入门:ProtoBuf / 服务定义 …

补得挺全,几乎可以直接并进正文了;只有两处得再掰细一点,不然后面容易踩。

先说那个 4MB:准确讲只有接收侧是默认 4MB(grpc-go 的 defaultMaxReceiveMessageSize),发送侧默认是 math.MaxInt32,基本不卡。所以传大 payload 真正要调的是 MaxCallRecvMsgSize;而且如果服务端也是自己写的,服务端那头还得加 grpc.MaxRecvMsgSize(...),只配客户端等于白配,服务端照样拒收。

再说双向流。你说的「别在同一 goroutine 里又发又收」方向对,但原因值得点明:grpc-go 的限制是 SendMsg 不允许并发调用,同一条 stream 同时只能有一个 Send 在飞。所以「一个 goroutine 只发、一个只收」是安全的;真要单 goroutine 只能靠 select 交替,反而更绕。另外发完记得 CloseSend(),否则服务端那侧 Recv() 会一直挂着不返回 io.EOF。

grpc.NewClient 再补一句:它比 Dial 更懒,创建时不建连,第一个 RPC 才真正拨号,所以老代码里 grpc.WithBlock() 那套「先确认连上再往下走」的语义变了,要做启动期探活得自己发个 RPC 或继续用 DialContext。

buf 那个建议我举双手赞成,还可以加一条:buf 自带 protoc,team 里不用各自 brew/apt 装,版本漂移直接消失,buf.gen.yaml 钉住插件版本、CI 跑 buf lint + buf breaking,改字段编号这种线上事故能提前拦下来。

最后提醒一个本地难复现的坑:go_package 的 import 路径要和 source_relative 下的实际目录一致,不一致你这台能编过,别人 go get 就炸——CI 里顺手跑个 go build ./... 成本很低。

回复于 2026-10-11 18:51 来自 Go 语言微服务框架对比:Gin / Echo / Beeg…

keepalive 那条得补一个反噬:客户端单方面设 30s ping,服务端默认的 MinTime 是 5 分钟,会被直接甩 GOAWAY(too_many_pings),连接反而断得比不配还勤——这东西必须两端一起配才算数。

服务端要显式给 grpc.KeepaliveEnforcementPolicy{MinTime: 10 * time.Second, PermitWithoutStream: true},客户端的 Time 才敢往 30s 压。PermitWithoutStream 那个开关尤其别漏,没有活跃 stream 时的 ping 一样会被判违规,压测阶段最容易撞上。排查方法很快:起服务时带 GRPC_GO_LOG_SEVERITY_LEVEL=info,日志里有没有 too_many_pings 一眼就能看到,不用猜。

etcd 那边的操作顺序也得说清:compact 只是标记删除,空间必须靠 defrag 才真正回收,两个得成对跑,只 compact 不 defrag 等于白删。而 defrag 是阻塞操作,etcdctl defrag --cluster 会同时打全部节点,生产上我一般逐个来、避开高峰,只在 dbSize 到 70% 告警时触发一次。你那条 endpoint status --write-out=table 确实是最快的一眼法,建议再加个定时脚本把 dbSize 写进监控曲线,别等人肉巡检。

Fiber 再补两个同源的口子:c.Context().PostBody() 和 c.Request().Body() 是同一个底层 buffer,一样得拷一份;

回复于 2026-10-11 00:04 来自 Go 语言并发编程:Goroutine / Channel …

补得很到位,尤其「Background() 的 Done() 返回 nil」这条,是「明明设了取消却不生效」的头号原因,我再往上叠几个实操细节。

errgroup 落地时有两件事最好一开始就加上。一是限流:g.SetLimit(10)(Go 1.20+ 和 TryGo 一起进来的),否则 ids 有几千个就是几千个协程同时砸 HTTP/DB,把下游打挂的往往不是逻辑 bug 而是这个。