-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- New backend allows to create a custom pipeline - Safe way to close channels
- Loading branch information
Showing
10 changed files
with
256 additions
and
247 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,61 +1,95 @@ | ||
package rbforwarder | ||
|
||
import "github.com/redBorder/rbforwarder/pipeline" | ||
import ( | ||
"time" | ||
|
||
"github.com/redBorder/rbforwarder/pipeline" | ||
) | ||
|
||
// Backend orchestrates the pipeline | ||
type Backend struct { | ||
sender pipeline.Sender | ||
senderPool chan chan *message | ||
input chan *message | ||
workers int | ||
queueSize int | ||
componentPools []chan chan *message | ||
input chan *message | ||
output chan *message | ||
|
||
working int | ||
} | ||
|
||
// NewBackend creates a new Backend | ||
func NewBackend(workers, queueSize, maxMessages, maxBytes int) *Backend { | ||
func NewBackend(input, output chan *message) *Backend { | ||
b := &Backend{ | ||
workers: workers, | ||
queueSize: queueSize, | ||
} | ||
|
||
b.senderPool = make(chan chan *message, b.workers) | ||
b.input = make(chan *message) | ||
|
||
return b | ||
} | ||
|
||
// Init initializes a backend | ||
func (b *Backend) Init() { | ||
for i := 0; i < b.workers; i++ { | ||
b.startSender(i) | ||
input: input, | ||
output: output, | ||
working: 1, | ||
} | ||
|
||
// Get messages from produces and send them to workers | ||
done := make(chan struct{}) | ||
go func() { | ||
done <- struct{}{} | ||
// Start receiving messages | ||
for m := range b.input { | ||
messageChannel := <-b.senderPool | ||
messageChannel <- m | ||
worker := <-b.componentPools[0] | ||
worker <- m | ||
} | ||
|
||
// When a close signal is received clean the workers. Wait for workers to | ||
// terminate | ||
b.working = 0 | ||
for _, componentPool := range b.componentPools { | ||
loop: | ||
for { | ||
select { | ||
case worker := <-componentPool: | ||
close(worker) | ||
case <-time.After(10 * time.Millisecond): | ||
break loop | ||
} | ||
} | ||
} | ||
|
||
// Messages coming too late will be ignored. If the channel is not set to | ||
// nil, late messages will panic | ||
b.output = nil | ||
|
||
// Send a close signal to message handler | ||
close(output) | ||
}() | ||
<-done | ||
|
||
Logger.Debug("Backend ready") | ||
return b | ||
} | ||
|
||
// Worker that sends the message | ||
func (b *Backend) startSender(i int) { | ||
sender := b.sender | ||
sender.Init(i) | ||
// PushComponent adds a new component to the pipeline | ||
func (b *Backend) PushComponent(c pipeline.Composer, w int) { | ||
index := len(b.componentPools) | ||
componentPool := make(chan chan *message, w) | ||
b.componentPools = append(b.componentPools, componentPool) | ||
|
||
workerChannel := make(chan *message) | ||
for i := 0; i < w; i++ { | ||
c.Init(i) | ||
|
||
go func() { | ||
for { | ||
b.senderPool <- workerChannel | ||
message := <-workerChannel | ||
sender.OnMessage(message) | ||
} | ||
}() | ||
worker := make(chan *message) | ||
b.componentPools[index] <- worker | ||
|
||
go func() { | ||
for m := range worker { | ||
c.OnMessage( | ||
m, | ||
func(m pipeline.Messenger) { | ||
if len(b.componentPools) >= index { | ||
nextWorker := <-b.componentPools[index+1] | ||
nextWorker <- m.(*message) | ||
} | ||
}, | ||
func(m pipeline.Messenger, code int, status string) { | ||
rbmessage := m.(*message) | ||
rbmessage.code = code | ||
rbmessage.status = status | ||
b.output <- rbmessage | ||
}, | ||
) | ||
|
||
if b.working == 1 { | ||
b.componentPools[index] <- worker | ||
} | ||
} | ||
}() | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
package pipeline | ||
|
||
// Next should be called by a component in order to pass the message to the next | ||
// component in the pipeline. | ||
type Next func(Messenger) | ||
|
||
// Done should be called by a component in order to return the message to the | ||
// message handler. Can be used by the last component to inform that the | ||
// message processing is done o by a middle component to inform an error. | ||
type Done func(Messenger, int, string) | ||
|
||
// Composer represents a component in the pipeline that performs a work on | ||
// a message | ||
type Composer interface { | ||
Init(int) error | ||
OnMessage(Messenger, Next, Done) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
Oops, something went wrong.