Warning: you are currently on v1 branch of GoFlow. v1 is a revisit and refactoring of the original GoFlow code which remained almost unchanged for 7 years. This branch is deep in progress, no stability guaranteed. API also may change.
If your code depends on the old implementation, you can build it using release 0.1.
--
GoFlow is a lean and opinionated implementation of Flow-based programming in Go that aims at designing applications as graphs of components which react to data that flows through the graph.
The main properties of the proposed model are:
If you don't have the Go compiler installed, read the official Go install guide.
Use go tool to install the package in your packages tree:
go get github.com/trustmaster/goflow
Then you can use it in import section of your Go programs:
import "github.com/trustmaster/goflow"
Below there is a listing of a simple program running a network of two processes.
This first one generates greetings for given names, the second one prints them on screen. It demonstrates how components and graphs are defined and how they are embedded into the main program.
package main
import (
"fmt"
"github.com/trustmaster/goflow"
)
// Greeter sends greetings
type Greeter struct {
Name <-chan string // input port
Res chan<- string // output port
}
// Process incoming data
func (c *Greeter) Process() {
// Keep reading incoming packets
for name := range c.Name {
greeting := fmt.Sprintf("Hello, %s!", name)
// Send the greeting to the output port
c.Res <- greeting
}
}
// Printer prints its input on screen
type Printer struct {
Line <-chan string // inport
}
// Process prints a line when it gets it
func (c *Printer) Process() {
for line := range c.Line {
fmt.Println(line)
}
}
// NewGreetingApp defines the app graph
func NewGreetingApp() *goflow.Graph {
n := goflow.NewGraph()
// Add processes to the network
n.Add("greeter", new(Greeter))
n.Add("printer", new(Printer))
// Connect them with a channel
n.Connect("greeter", "Res", "printer", "Line")
// Our net has 1 inport mapped to greeter.Name
n.MapInPort("In", "greeter", "Name")
return n
}
func main() {
// Create the network
net := NewGreetingApp()
// We need a channel to talk to it
in := make(chan string)
net.SetInPort("In", in)
// Run the net
wait := goflow.Run(net)
// Now we can send some names and see what happens
in <- "John"
in <- "Boris"
in <- "Hanna"
// Send end of input
close(in)
// Wait until the net has completed its job
<-wait
}
Looks a bit heavy for such a simple task but FBP is aimed at a bit more complex things than just printing on screen. So in more complex an realistic examples the infractructure pays the price.
You probably have one question left even after reading the comments in code: why do we need to wait for the finish signal? This is because flow-based world is asynchronous and while you expect things to happen in the same sequence as they are in main(), during runtime they don't necessarily follow the same order and the application might terminate before the network has done its job. To avoid this confusion we listen for a signal on network's wait
channel which is sent when the network finishes its job.
Here are some Flow-based programming terms used in GoFlow:
More terms can be found in Flow-based Wiki Terms and FBP wiki.
Documentation for the flow package can be accessed using standard godoc tool, e.g.
godoc github.com/trustmaster/goflow
Here are related projects and resources: