如何在 Go 中创建动态管道路由

在观看了一些关于 Go 的视频教程后,我想知道是否可以创建具有动态路由的管道。受 Go 中的 Builder 设计模式和 Optional Pattern 的启发,我想出了一个想法来实现它。

怎么做 ?

想象一下我们在一家汽车厂,一种车型有几种类型。在生产同一车型的汽车时,会有一些相同的制造步骤,例如底盘和车身的制造,然后每种类型都会添加一组不同的功能。我们将创建的管道有一个如下所示的流程图,在“车身”管道之后,可以直接构建汽车或根据其类型添加功能。

动态路线图

管道包

首先,我们将创建一个汽车结构,其中包含几个字段,例如基础、车身、特征 A、特征 B 和特征 C。之后,我们将创建几个方法来填充这些字段。

var Process = func(channel chan *CarBuilder) func(builder *CarBuilder) {
	return func(builder *CarBuilder) {
		channel <- builder
	}
}

type NextProcess func(builder *CarBuilder)

type CarBuilder struct {
	car
	Next []NextProcess
}

func (c *CarBuilder) nextProcess() {
	next := c.Next[0]
	c.Next = c.Next[1:]
	next(c)
}

func (c *CarBuilder) Build() car {
	return c.car
}

之后,我们定义了一个受可选模式启发的名为“Process”的变量,一个结构“CarBuilder”成为汽车的构建器,其中包含汽车字段和我们要添加的功能列表。然后创建一个“nextProcess”方法来移动到下一个管道,并创建一个“Build”方法来返回我们建造的汽车。

var Process = func(channel chan *CarBuilder) func(builder *CarBuilder) {
	return func(builder *CarBuilder) {
		channel <- builder
	}
}

type NextProcess func(builder *CarBuilder)

type CarBuilder struct {
	car
	Next []NextProcess
}

func (c *CarBuilder) nextProcess() {
	next := c.Next[0]
	c.Next = c.Next[1:]
	next(c)
}

func (c *CarBuilder) Build() car {
	return c.car
}

最后一个阶段是定义管道,我们将创建 5 个管道,分别是“BaseBuilder”、“BodyBuilder”、“FeatureABuilder”、“FeatureBBuilder”和“FeatureCBuilder”,用于创建汽车模型的基础或添加特征。

func BaseBuilder(channel <-chan *CarBuilder, nextChannel chan<- *CarBuilder) {
	for {
		carBuilder := <-channel
		carBuilder.setBase(true)
		nextChannel <- carBuilder
	}
}

func BodyBuilder(channel <-chan *CarBuilder) {
	for {
		carBuilder := <-channel
		carBuilder.setBody(true)
		carBuilder.nextProcess()
	}
}

func FeatureABuilder(channel <-chan *CarBuilder) {
	for {
		carBuilder := <-channel
		carBuilder.setFeatureA(true)
		carBuilder.nextProcess()
	}
}

func FeatureBBuilder(channel <-chan *CarBuilder) {
	for {
		carBuilder := <-channel
		carBuilder.setFeatureB(true)
		carBuilder.nextProcess()
	}
}

func FeatureCBuilder(channel <-chan *CarBuilder) {
	for {
		carBuilder := <-channel
		carBuilder.setFeatureC(true)
		carBuilder.nextProcess()
	}
}

主功能

要在主函数(main.go)中使用它,首先我们定义一个“等待组”来等待所有进程完成,并为每个具有大缓冲区的管道定义一个通道以加快速度:D

var wg sync.WaitGroup
baseChan := make(chan *pipe.CarBuilder, 1000)
bodyChan := make(chan *pipe.CarBuilder, 1000)
featureAChan := make(chan *pipe.CarBuilder, 1000)
featureBChan := make(chan *pipe.CarBuilder, 1000)
featureCChan := make(chan *pipe.CarBuilder, 1000)
buildChan := make(chan *pipe.CarBuilder, 1000)

还要准备一份我们想要的汽车类型的清单以及这些类型的可用功能,以促进大规模制造汽车。“NextProcess” 切片包含表示我们创建类型所需的特征的通道。

typeStandard := []pipe.NextProcess{
  pipe.Process(buildChan),
}

typeA := []pipe.NextProcess{
  pipe.Process(featureAChan), 
  pipe.Process(featureCChan), 
  pipe.Process(buildChan),
}

typeB := []pipe.NextProcess{
  pipe.Process(featureAChan), 
  pipe.Process(featureBChan), 
  pipe.Process(buildChan),
}

typeC := []pipe.NextProcess{
  pipe.Process(featureBChan), 
  pipe.Process(featureCChan), 
  pipe.Process(buildChan),
}

typeFullFeature := []pipe.NextProcess{
  pipe.Process(featureAChan), 
  pipe.Process(featureBChan), 
  pipe.Process(featureCChan), 
  pipe.Process(buildChan),
}

features := [][]pipe.NextProcess{
	typeStandard, typeA, typeB, typeC, typeFullFeature,
}

之后,定义我们想要的工作池,我为每个管道使用 4 个,因为如果使用更多(在我的设备中),则不会显着增加。我们还可以定义一个汽车计数器来确定已经制造了多少辆汽车。

var carCount int64
for i := 1; i <= 4; i++ {
   go pipe.BaseBuilder(baseChan, bodyChan)
   go pipe.BodyBuilder(bodyChan)
   go pipe.FeatureABuilder(featureAChan)
   go pipe.FeatureBBuilder(featureBChan)
   go pipe.FeatureCBuilder(featureCChan)
   go func() {
      for {
         readyToBuild := <-buildChan
         readyToBuild.Build()
         wg.Done()
         atomic.AddInt64(&carCount, 1)
      }
   }()
}

定义我们想要制造的汽车数量及其类型,在这种情况下,我们将制造 100000 辆汽车。

var testBuilds []pipe.CarBuilder
for i := 0; i < 1000000; i++ {
   testBuilds = append(testBuilds, pipe.CarBuilder{
      Next: features[i%5],
   })
}

最后,将我们上面定义的汽车一个一个地放入第一个管道“BaseBuilder”中,让奇迹发生?

defer func() func() {
   start := time.Now()
   return func() { fmt.Println(time.Since(start), carCount) }
}()()
for _, testBuild := range testBuilds {
   wg.Add(1)
   t := testBuild
   baseChan <- &t
}
wg.Wait(

在 go 中创建动态管道路由的关键是什么?

关键在于“Process”变量和“nextProcess”方法。在 process 变量中,我们可以输入要访问的管道的通道,而“nextProcess”方法会将 CarBuilder 指针传递给“Process”变量,这意味着它将 CarBuilder 指针发送到下一个管道。

var Process = func(channel chan *CarBuilder) func(builder *CarBuilder) {
	return func(builder *CarBuilder) {
		channel <- builder
	}
}

type NextProcess func(builder *CarBuilder)

type CarBuilder struct {
	car
	Next []NextProcess
}

func (c *CarBuilder) nextProcess() {
	next := c.Next[0]
	c.Next = c.Next[1:]
	next(c)
}

func (c *CarBuilder) Build() car {
	return c.car
}

我希望你们中的一些人觉得这很有帮助。如果您有兴趣查看所有源代码,可以在 https://github.com/yudaph/dynamic-pipeline.