一次模块优化——基于Go chan的流水线模式

Go团队规模化落地的四大工程支柱 Go语言以语法简洁著称,但其工程化落地远不止于安装编译器或运行Hello World。核心在于理解模块化(module)机制与依赖管理原理,通过可复现构建、统一工具链和自动化规范保障协作确定性。技术价值体现在降低CI失败率、压缩新人上手时间、提升二进制可审计性;典型应用场景包括微服务重构、数据管道重写及云原生基础设施建设。实践中,‘go环境配置’与‘go mod download’等高频问题,本质是环境治理与依赖信任链缺失所致——本文聚焦团队级Go工程化的四个关键支柱:环境治理、代码规范、依赖管理与认知对齐 阅读详情

背景

最近有一个文本切分的模块,主要作用是将一段文本切分成单句。仿照重构之前的版本使用正则表达式去匹配标点符号,然后按照「单句文本长度最大值」作为阈值,将整段文本切分为多个句子。大致的代码如下:

// 按句号分割
func splitWithPeriod(text string) []string {
    reg := regexp.MustCompile(`([\n\t])([^”"’])`)
	// use reg to match newline and tab space
}
// 按逗号分割
func splitWithComma(text string) []string {
	reg := regexp.MustCompile(`([,,])([^”"’])`)
	// use reg to match comma
}

func splitForce(text string) []string {
    // ....
}

func Split(text string) []string {
    var results []string
    for _, v := range splitWithPeriod(text) {
        if len(v) < maxLen {
            results = append(results, v)
            continue 
        }
        for _, vv := range splitWithComma(v) {
            if len(vv) < maxLen {
                results = append(results, vv)
                continue
            }
            for _, vvv := range splitForce(vv) {
                results = append(results, vvv)
            } 
        }
    }
    return results;
}

上述代码运行逻辑完全是OK的,但后来线上环境遇到一个英文文本的请求,在做切分的时候会遇到很长一段英文,中间没有标点符号。导致有单词被切割开来,例如 hello -> hel, lo,这显然是不能接受的。因此有必要加上按空格进行划分,因此 Split 函数变为:

func Split(text string) []string {
    var results []string
    ...
    		
            for _, vvv := range splitWithSpace(vv) {
            	if len(vvv) < maxLen {
            		results = append(results, vvv)	
            	}
                for _, vvvv := range splitForce(vvv) {
                	results = append(results, vvv)
                }
            } 
        }
    }
    return results;
}

好吧,现在就有点接受不了了,Split 函数圈复杂度太高了!如果后续还要接入其他的分割函数,维护起来不是很难受?这不能忍,不想自己写的这份代码日后慢慢变成“屎山”,立即重构!

优化方案

简单分析一下 splitWithComma, splitWithPeriod, splitWithSpace 这些函数的定义形式是一致的,有没有办法将这些函数合并缩减到一到两个。仔细思考一下发现是不行的,因为在文本分割里面,标点符号是存在优先级的 Period > Comma > Space ,也就是如果句号可以将整段文字分开就不必使用逗号分割。

这些分割函数 splitWithXXX 都是按照优先级进行流水线式的处理方式,这样就可以按照设计模式中的「责任链」模式设计:

type Splitter interface {
    Execute(string) []string
    SetNext(Splitter)
}

type basicSplitter struct {
    next Splitter
}

func (this *BasicSplitter) SetNext(s Splitter) {
    this.next = s
}

type splitterWithComma struct {
    basicSplitter
}

func (this *splitterWithComma) Execute(text string) []string {
	// use reg to match newline and tab space
	var results []string
    for _, v := range splitWithComma(text) {
        if len(v) < maxLen {
            results = append(results, vv)
        }
        results = append(results, next.execute(v)...)
    }
    return results;
}

......

func Split(text string) []string {
    periodSplitter := &splitterWithperiod{}
    commaSplitter := &splitterWithComma{}
    spaceSplitter := &splitterWithSpace{}
    ......
    // set next
    periodSplitter.SetNext(commaSplitter)
    commaSplitter.SetNext(periodSplitter)
    ......
    
    return periodSplitter.Execute(text)
}

嗯,圈复杂度是下降了,但是平白无故多了许多接口,而且引入了责任链设计模式,引入的额外代码体量几乎赶上来业务代码,还是不够优雅!

想起来之前在《Go语言精进之路》书上看过使用 chan 实现的 pipline 模式,查阅一下,结合业务逻辑,大致的代码如下:

type splitter func(string) []string
func spawn(do splitter, in <-chan string) <- chan string {
    out := make(chan string)
    go func() {
    	for v := range in {
            out <- do(v)
        }    
        close(out)
    }()
    return out
}

// usage
in := make(chan string)
go func() {
    for _, v := range texts {
    	in <- v   
    }
    close(in)
}()
out := spawn(splitWithSpace, spawn(splitWithComma, spawn(splitWithPeriod, in)))
var results []string
for v := range out {
    results = append(results, v)
}

嗯,这样看起来就简洁明了了,而且后面再引入其他的分割函数,也能很轻易地加入进来,相比之前的代码,业务逻辑直观了许多,并且同步处理也转为了异步处理,理论上是提升了该程序的性能。

此外,考虑到分割函数中很多都是按照正则表达式进行匹配的,尝试将其提取出来作为全局变量,以免每次调用都需要生成一个 Regexp 对象,而且从 go.doc 也能得知 Regexp 的操作基本是线程安全的:

A Regexp is safe for concurrent use by multiple goroutines, except for configuration methods.

通过 go test -bench 可以得知重构前后的性能对比结果:

# 重构后
[1]
1032	   1301266 ns/op	  286533 B/op	     974 allocs/op
PASS
ok  		1.470s
[2]
886	   1267392 ns/op	  284943 B/op	     975 allocs/op
PASS
ok  		1.269s
[3]
1017	   1215578 ns/op	  287777 B/op	     975 allocs/op
PASS
ok  		1.364s

重构前
[1]
BenchmarkSplit-8   	     914	   1250922 ns/op	  530060 B/op	    3325 allocs/op
PASS
ok  		1.284s
[2]
BenchmarkSplit-8   	     908	   1464555 ns/op	  531797 B/op	    3325 allocs/op
PASS
ok  		2.487s
[3]
BenchmarkSplit-8   	     981	   1605954 ns/op	  531603 B/op	    3325 allocs/op
PASS
ok  		2.648s

无论是代码整洁程度还是性能来看,这次重构都是令人振奋的!(叉会腰~)

参考

  • 《Go语言精进之路》第33.2条 Go常见的并发模式
  • Go regexp
Go新手首程生存指南:从package main到可执行文件 Go语言的程序本质是静态编译的包模型,其执行起点严格依赖package main声明与func main()函数签名,这决定了它既非脚本也非动态加载式语言。理解import的编译期强依赖机制、go run与go build的工具链分工、以及go mod模块化对依赖锁定和版本可重现性的保障,是构建可靠Go工程的基础。这些设计共同支撑起Go在微服务、CLI工具、嵌入式及Serverless等场景中的高确定性交付能力——本文聚焦新手写第一个Go程序时必遇的核心契约:包结构、入口规范、依赖声明与二进制生成。 阅读详情

相关推荐

字节跳动Go网络库高性能优化实践解析

网络库作为分布式系统的核心基础设施,其性能直接影响微服务通信、实时推送等关键业务场景的稳定性。传统基于goroutine-per-connection的模型在十万级并发时会出现内存暴涨、延迟抖动等问题。通过事件驱动架构重构IO处理流程,结合零拷贝技术减少数据拷贝开销,配合智能调度算法动态调整资源分配,可实现百万级长连接的稳定支撑。字节跳动在Go网络库优化中创新性地应用内存池化、协议优化等手段,使单机吞吐量提升3倍以上,为直播连麦、即时通讯等高并发场景提供了技术保障。这些实践对构建高性能网络中间件具有重要参考

weixin_30388677的博客 458

Go语言gRPC流式通信机制与性能优化实践

gRPC作为现代微服务架构的核心通信协议,其流式通信模式通过HTTP/2的多路复用能力实现了高效的双向数据传输。在Go语言生态中,goroutine的轻量级线程模型与流式接口天然契合,开发者可以通过server-side streaming、client-side streaming和bidirectional streaming三种模式构建实时数据处理系统。本文深入解析了流控窗口、连接池优化等关键技术原理,特别针对Go语言特性提供了内存池复用、bufio缓冲等工程实践方案,并探讨了在实时监控、机器学习推理

weixin_33691700的博客 398

告别环境配置焦虑:在Win10的Ubuntu子系统里搞定CSAPP实验环境(附32/64位库编译指南)

本文详细介绍了如何在Win10的Ubuntu子系统中搭建CSAPP实验环境,包括WSL 2的安装与配置、32/64位库的编译指南以及高效开发工具链的设置。通过优化WSL性能和使用实用技巧,开发者可以轻松应对CSAPP实验中的环境配置问题,提升学习效率。

weixin_30443813的博客 296

Go语言高效文件处理:协程与管道实战

并发编程是现代软件开发的核心技术之一,Go语言通过goroutine和channel提供了独特的并发模型实现。goroutine作为轻量级线程,创建成本低且由运行时高效调度;channel则实现了安全的协程间通信。这种并发范式特别适合构建高性能IO密集型应用,如日志处理、数据流水线等场景。本文以文件处理为例,展示了如何结合Go的文件操作API与并发特性,构建高效的处理流水线。通过合理的并行度控制、内存管理和错误处理,开发者可以显著提升程序吞吐量。这种技术组合在实际工程中已证明能有效提升资源利用率,是处理大规

congji3817的博客 353

MTK平台TP驱动设备树与GPIO配置实战解析

本文详细解析了MTK平台TP驱动开发中的设备树与GPIO配置实战。通过DCT工具配置GPIO引脚,结合设备树节点定义触摸参数,并分享驱动移植、编译调试技巧及常见问题解决方案,帮助开发者快速掌握MTK触摸驱动开发核心。

ice55的博客 233

408——计算机组成原理(持续更新)

小数点前的0代表正数,1代表负数。

qq_55202378的博客 1721

go并发模式之----流水线管道模式

golang并发模式

不忘初心,才能始终 570

【影像组学】用3Dslicer或Python提取影像组学特征

用3Dslicer或Python提取影像组学特征

abdslfwoeugp 1万+

Go 流水线编程模式

流水线工作模型在工业领域内十分常见,它将工作流程分为多个环节,每个环节根据工作强度安排合适的人员数量。良好的流水线设计尽量让各环节的流通率平衡,最大化提高产能效率。Go 是一门实用性语言,...

slphahaha的博客 534

CarMaker快速入门

本合集是小明师兄推出的CarMaker汽车仿真,CarMaker无论是传统动力学、动力系统开发、底盘系统开发还是目前智能驾驶辅助系统及智能驾驶系统开发有着非常重要的作用,可以有效节省开发周期和开发成本,基于仿真快速实现算法开发和系统验证。...

小明师兄的博客 1万+

【Bug已解决】Claude Code Auto-update failed 自动更新失败的解决方案

方案适用场景推荐指数清理残留重新安装已经损坏,需要快速恢复可用⭐⭐⭐⭐⭐修复目录权限权限不足导致更新反复失败⭐⭐⭐⭐⭐关闭自动更新网络/安全软件环境不稳定⭐⭐⭐切换原生安装方式想从根源规避 npm 更新问题⭐⭐⭐⭐自检修复脚本团队/服务器环境需要自动化恢复⭐⭐⭐报错的本质是自动更新过程中的文件替换操作因权限不足或被中断而处于不完整状态,而不是 Claude Code 本身的功能缺陷。遇到问题先清理残留文件再重新安装,不要在损坏状态上反复尝试修复;

一键难忘的博客 1万+

java设计模式---采用Pipeline处理器流水线实现责任链模式

搞一个类似于netty的Pipeline责任链

初入江湖,多多关照 2148

畅游Diffusion数字人(0):专栏文章导航

畅游Diffusion数字人(0):专栏文章导航

沉迷单车的追风少年 1421

Go并发模型:流水线模型

Go作为一个实用主义的编程语言,非常注重性能,在语言特性上天然支持并发,Go并发模型有多种模式,通过流水线模型系列文章,你会更好的使用Go的并发特性,提高的程序性能。 这篇文章主要介绍流水线模型的流水线概念,后面文章介绍流水线模型的FAN-IN和FAN-OUT,最后介绍下如何合理的关闭流水线的协程。 Golang的并发核心思路 Golang并发核心思路是关注数据流动。数据流动的过程交给channel,数据处理的每个环节都交给goroutine,把这些流程画起来,有始有终形成一条线,那就能构成流水线模型。 但

稻草人技术博客 1344

青岛鼎信Java开发面试题及参考答案(3万字长文,多张原理图)

MySQL 的事务特性有哪些? MySQL 事务具有四个重要特性,被称为 ACID 特性。 原子性(Atomicity):事务是一个不可分割的工作单位,事务中的操作要么全部执行,要么全部不执行。例如,在银行转账场景中,从账户 A 转账到账户 B,包括扣减账户 A 的金额和增加账户 B 的金额这两个操作必须作为一个整体来完成。如果在执行过程中发生错误,比如系统崩溃或者数据库出现故障,那么已经执行的部分必须全部回滚,就好像整个转账操作从未发生过一样。 一致性(Consistency):事务必须

大模型大数据攻城狮的专栏 1088

TMS_WebGMaps_1.9.8.0_Full_Source

TMS_WebGMaps_1.9.8.0_Full_Source 全源码 delphi cbuilder

11 | 并发模式:Go 语言中即学即用的高效并发模式

11 | 并发模式:Go 语言中即学即用的高效并发模式 如何通过 Context 实现日志跟踪? 要想跟踪一个用户的请求,必须有一个唯一的 ID 来标识这次请求调用了哪些函数、执行了哪些代码,然后通过这个唯一的 ID 把日志信息串联起来。这样就形成了一个日志轨迹,也就实现了用户的跟踪,于是思路就有了。 在用户请求的入口点生成 TraceID。 通过 context.WithValue 保存 TraceID。 然后这个保存着 TraceID 的 Context 就可以作为参数在各个协程或者函数间传

剑心!的博客 445

怀旧版电脑GBC模拟器

再现原版GBC掌机的界面,电脑运行连游戏界面都一样大小,很怀旧,很真实,(英文版)强烈推荐,很小的文件,很难才找到的。

Google Cloud Go SDK实战指南:深入解析golang-samples的架构设计与最佳实践

Google Cloud Go SDK官方示例库golang-samples为开发者提供了丰富的云原生应用开发实践参考。本文将从架构设计、性能优化、安全防护等多个维度,深入分析如何基于这个项目构建高效可靠的云服务应用。 ## 问题:云原生应用开发的复杂性挑战 现代云原生应用开发面临诸多挑战:复杂的认证流程、分布式系统设计、数据安全处理、性能调优等。许多开发者在集成Google Cloud服务时

gitblog_00009的博客 1052

lwip-2.0.3

lwip是瑞典计算机科学院(SICS)的Adam Dunkels 开发的一个小型开源的TCP/IP协议栈。实现的重点是在保持TCP协议主要功能的基础上减少对RAM 的占用。

数学建模模型总结

这是本人在两年内准备参加数学建模竞赛搜集到的模型资料,已经经过了本人的筛选和整理,希望能够对准备参加数学建模竞赛的学弟学妹们提供一些帮助!

Golang日志监控系统设计与性能优化实践

日志监控是现代分布式系统运维的核心基础设施,其核心原理是通过实时采集、解析和分析日志数据,实现系统状态的可观测性。Golang凭借其高并发协程模型和内存安全特性,成为构建高性能日志监控系统的理想选择。在技术实现层面,采用分层架构设计(采集→传输→处理→存储)可有效应对海量日志处理挑战,结合Kafka、Elasticsearch等中间件可实现每秒10万+条日志的处理能力。典型应用场景包括实时错误检测、性能瓶颈分析和安全事件追踪,在电商、金融等行业实践中可将故障定位时间从小时级缩短至分钟级。通过合理的Gorou

weixin_33696822的博客 318

Golang重构Brutespray:高并发架构设计与安全工具开发实践

在网络安全和渗透测试领域,自动化工具的开发常面临性能与并发处理的挑战。传统基于Python的脚本工具虽然灵活,但在处理大规模任务时,往往受限于解释器性能和复杂的依赖环境。Golang凭借其原生的goroutine和channel机制,为构建高并发、高性能的独立二进制工具提供了理想解决方案。其核心原理在于轻量级线程(goroutine)与通信管道(channel)的结合,实现了高效的任务调度与资源管理,显著提升了工具的执行效率和可维护性。这种技术价值体现在能够轻松管理成千上万的并发任务,同时通过静态编译确保部

dingdi3021的博客 438

Go for循环底层原理与性能优化实战

for循环是编程语言中最基础的控制流结构,其本质是编译器对跳转逻辑、迭代协议和状态机的静态建模。理解for的三种形态(ForClause、RangeClause、无条件for)对应的不同汇编生成逻辑,是掌握Go性能调优的关键前提。Go通过单一关键字承载全部循环语义,依赖编译期分析消除运行时歧义,带来零成本抽象的同时,也将执行意图完全交由开发者声明。这使得for range map的哈希遍历开销、for i < len(s)的重复计算陷阱、以及for { select {} }的goroutine泄漏风险,都

weixin_33785108的博客 318

深入解析dry终端Docker监控工具:架构设计与性能优化实战

dry是一款面向终端环境的Docker容器管理工具,专为追求高效工作流的开发者和运维工程师设计。作为命令行界面的Docker管理解决方案,dry通过简洁直观的终端UI实现了容器监控、资源统计和集群管理的完整功能栈,在保持轻量级特性的同时提供了生产级监控能力。 ## 技术架构设计思路 ### 基于Bubble Tea的现代化终端UI框架 dry的核心架构建立在Charm生态系统的Bubble

gitblog_00273的博客 767

TypeScript Go:用Go语言重写TypeScript编译器,性能提升3倍

TypeScript Go是一个革命性的开源项目,它用Go语言重新实现了TypeScript编译器,为前端开发带来了前所未有的性能突破。这个项目不仅保留了TypeScript的所有特性,还通过Go语言的并发能力和原生性能,将编译速度提升了3倍以上,让大型项目的构建时间从分钟级缩短到秒级。 ## 🚀 为什么你需要关注TypeScript Go? ### 性能瓶颈的终极解决方案 传统的Type

gitblog_01008的博客 816

ZCode Dogfood 报告模板实战:用可复现证据驱动 Web 应用质量验收

本篇技术指南聚焦 ZCode 仓库内 `.agents/skills/dogfood` 技能体系中的报告模板——[dogfood-report-template.md](https://link.gitcode.com/i/17b45db9899e56bfba8634addf9eab8b),讲解如何用它产出结构化、可复现的 Dogfood(自测/试吃)质量报告。读者将掌握报告头部元数据、严重度汇总

gitblog_01083的博客 824

Go语言PGO实战:基于运行时剖析的智能编译器优化指南

编译器优化是提升程序性能的核心技术之一,它通过分析代码结构来改进执行效率。传统优化如内联和逃逸分析基于静态分析,而Profile-Guided Optimization(PGO)则引入了动态运行时数据作为优化依据。其原理是在程序运行时收集热点函数、分支频率等剖析信息,指导编译器进行针对性优化,例如更精确的内联决策和代码布局调整。这项技术的价值在于它能根据实际负载模式生成更高效的机器码,尤其适用于存在明确热点路径的CPU密集型应用。在Go语言中,PGO已从1.21版本起成为稳定功能,通过集成pprof采集的C

weixin_34161083的博客 664

TypeScript 7.0 Go语言重写编译器:10倍性能提升架构解析

编译器作为现代软件开发的核心工具,其性能直接影响开发效率。传统编译器在处理大型项目时往往面临内存管理和并行处理的技术瓶颈。TypeScript 7.0通过采用Go语言重写编译器架构,充分利用goroutine并发模型和高效垃圾回收机制,实现了编译性能的质的飞跃。这种架构变革不仅解决了大型代码库的编译速度问题,更为前端工程化提供了新的技术范式。在实际应用中,Go语言编译器的并行处理流水线和智能增量编译机制,使得TypeScript在大型企业级项目中能够实现10倍以上的性能提升,显著优化开发体验。

weixin_34268579的博客 414

Golang在数学建模中的工程化实践:从环境配置到安全交付

数学建模已超越传统算法实现,进入高并发、可部署、强合规的工程化阶段。Golang凭借静态编译、原生并发、内存可控和安全工具链等特性,成为连接数学逻辑与生产系统的理想桥梁。其核心价值体现在环境可复现(Go Modules/CGO_ENABLED/交叉编译)、模型可验证(接口抽象+参数配置+竞速调度)、系统可审计(输入校验+依赖扫描+结构化日志)三大维度。尤其在量化回测、实时优化、多目标求解等典型场景中,Go显著提升P99延迟稳定性与资源利用率。本文聚焦真实竞赛与工业项目中的Go建模实践,覆盖环境筑基、模型封装

weixin_33851177的博客 351
上一篇: 练习时长半年的Git命令小结
下一篇: 流式输出场景下的字符串替换——一次颇具挑战性的需求
Corleo
博客等级 码龄11年 120粉丝 32原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值