From 77f8289e46e835bea8fcbbb674e2cdcbdd532ccf Mon Sep 17 00:00:00 2001 From: Diogo Behrens Date: Wed, 7 Feb 2018 12:09:40 +0100 Subject: [PATCH 1/5] messaging example --- examples/3-messaging/README.md | 464 ++++++++++++++++++ examples/3-messaging/blocker/blocker.go | 70 +++ examples/3-messaging/cmd/block-user/main.go | 34 ++ examples/3-messaging/cmd/loadgen/main.go | 86 ++++ examples/3-messaging/cmd/processor/main.go | 58 +++ examples/3-messaging/cmd/service/main.go | 22 + .../3-messaging/cmd/translate-word/main.go | 35 ++ examples/3-messaging/collector/collector.go | 54 ++ examples/3-messaging/detector/detector.go | 80 +++ .../3-messaging/figs/goka-arch-blocker.png | Bin 0 -> 99066 bytes .../3-messaging/figs/goka-arch-simple.png | Bin 0 -> 68302 bytes examples/3-messaging/filter/filter.go | 55 +++ examples/3-messaging/message.go | 41 ++ examples/3-messaging/service/service.go | 84 ++++ examples/3-messaging/translator/translator.go | 32 ++ 15 files changed, 1115 insertions(+) create mode 100644 examples/3-messaging/README.md create mode 100644 examples/3-messaging/blocker/blocker.go create mode 100644 examples/3-messaging/cmd/block-user/main.go create mode 100644 examples/3-messaging/cmd/loadgen/main.go create mode 100644 examples/3-messaging/cmd/processor/main.go create mode 100644 examples/3-messaging/cmd/service/main.go create mode 100644 examples/3-messaging/cmd/translate-word/main.go create mode 100644 examples/3-messaging/collector/collector.go create mode 100644 examples/3-messaging/detector/detector.go create mode 100644 examples/3-messaging/figs/goka-arch-blocker.png create mode 100644 examples/3-messaging/figs/goka-arch-simple.png create mode 100644 examples/3-messaging/filter/filter.go create mode 100644 examples/3-messaging/message.go create mode 100644 examples/3-messaging/service/service.go create mode 100644 examples/3-messaging/translator/translator.go diff --git a/examples/3-messaging/README.md b/examples/3-messaging/README.md new file mode 100644 index 00000000..c2d5bee9 --- /dev/null +++ b/examples/3-messaging/README.md @@ -0,0 +1,464 @@ +# A long example: Building a messaging service + +In this example, we build a ficticious messaging service that employs several features available in Goka. +Our main goal is to explore all methods available in `Context` and to illustrate how components can be composed with joins and lookups. + +The messaging service offers only two endpoints: + +1. `localhost:8080/{user}/send` +2. `localhost:8080/{user}/feed` + +The *user* in the URLs refers to the user performing the request. +The *send* endpoint takes JSON data containing the recipient and the content of a message and emits it to Kafka. +The *feed* endpoint shows the latest 5 messages received by the user. + +We will develop the example in 4 steps, building the pipeline between both endpoints: + +1. We start with a simple implementation that collects emitted messages in a table. +2. Next, we add the capability of blocking annoying users. +3. We then introduce a word translator to help (or amuse) users. +4. Finally, we block annoying users automatically with a simplistic spam detector. + +## 1. Basic components and features + +Goka provides three components to build systems: emitters, processors, and views. +The following figure depicts our initial design using these three components together with Kafka and the endpoints. + +![Architecture](figs/goka-arch-simple.png) + +The architecture here follows the approach in [this blog post](https://tech.lovoo.com/2017/05/23/goka/). + +### Send endpoint + +The main type we will be dealing with is the [`Message`](message.go#L14) type: + +```go +type Message struct { + From string + To string + Content string +} +``` + +If Bob wants to send a message to Alice, he would send a request to the send endpoint with the recipient and the content of the message. +For example: + +```bash +curl -X POST \ + -d '{"to": "Alice", "content": "Hey, how are you doing?"}' \ + http://localhost:8080/Bob/send +``` + +The send handler parses message type, completing the `From` field. +Afterwards, it emits the message into the `ReceivedStream` topic using the receiver user as key (`m.To`): + +```go +func send(emitter *goka.Emitter) func(w http.ResponseWriter, r *http.Request) { + return func(w http.ResponseWriter, r *http.Request) { + m := messaging.Message{"From": mux.Vars(r)["user"]} + b, _ := ioutil.ReadAll(r.Body) + json.Unmarshal(b, &m) + emitter.EmitSync(m.To, &m) + } +} +``` + +The emitter is configured to only emit into the `ReceivedStream` topic and to use `MessageCodec` to encode the message `m`. +This is how the emitter is [created](service/service.go#L24): +```go +emitter, _ := goka.NewEmitter( + brokers, + messaging.ReceivedStream, + new(messaging.MessageCodec), +) +router.HandleFunc("/{user}/send", send(emitter)).Methods("POST") +``` + +Note we are ignoring errors for the sake of readability. +The complete example in the repository handles them, though. + +### `Context.Value()` and `Context.SetValue()`: collecting messages + +We define the *collector table* to contain the latest 5 messages received by each user. +The *collector processor* keeps the table up-to-date by consuming `ReceivedStream`. +The collector callback is defined as [follows](collector/collector.go#L29): + +```go +func collect(ctx goka.Context, msg interface{}) { + var ml []messaging.Message + if v := ctx.Value(); v != nil { + ml = v.([]messaging.Message) + } + + m := msg.(*messaging.Message) + ml = append(ml, *m) + + if len(ml) > maxMessages { + ml = ml[len(ml)-maxMessages-1:] + } + ctx.SetValue(ml) +} +``` + +The `ctx` is scoped with the key of the message -- we used the receiver as key in the emitter. +With `ctx.Value()` we fetch the table value for the key. +The value is a slice of messages. +We then append the received message and cap the length of the slice with the constant `maxMessages`, which is 5. +Finally, we store the value back in the table with `ctx.SetValue()`. + +### Feed endpoint + +When Alice wants to see her 5 latest received messages, she requests that from the feed endpoint. +For example: +``` +$ curl localhost:8080/Alice/feed +Latest messages for Alice +0 Bob: Hey, how are you doing? +1 Charlie: See you later. +``` + +The handler employs a view on `collector.Table` to retrieve the messages for alice. +It gets the user from the URL and tries to get the value from the view. +If no value is available, the user has received no messages yet. +Otherwise, the handler loops over the messages in the slice and formats the output. + +```go +func feed(view *goka.View) func(w http.ResponseWriter, r *http.Request) { + return func(w http.ResponseWriter, r *http.Request) { + user := mux.Vars(r)["user"] + val, _ := view.Get(user) + if val == nil { + fmt.Fprintf(w, "%s not found!", user) + return + } + messages := val.([]messaging.Message) + fmt.Fprintf(w, "Latest messages for %s\n", user) + for i, m := range messages { + fmt.Fprintf(w, "%d %10s: %v\n", i, m.From, m.Content) + } + } +} +``` + +When creating the view, it is configured to watch the `collector.Table` and use `collector.MessageListCodec` to decode table values. + +```go +view, _ := goka.NewView( + brokers, + collector.Table, + new(collector.MessageListCodec), +) +go view.Start() +router.HandleFunc("/{user}/feed", feed(view)).Methods("GET") +``` + +`MessageListCodec` simply encodes and decodes slices of `Message`s into and from JSON (see [implementation](message.go#L31) for details). + +### Running the example + +To make the system actually run, we still have to decide how to combine these components. +One option is to start emitter, processor, and view all in the same Go program, but that would make the system inflexible because we cannot scale the components independently. +We can also have each component running in its own Go program, but that may complicate deployment. +In this example, we put the endpoint handlers and, consequently, emitter and view in the same Go program. +In another Go program, we start the collector processor. +This solution allows us to start, stop, and scale them independently. + +To start the service in a terminal, change directory to `examples/3-messaging` and type: + +```sh +go run cmd/service/main.go # start endpoint handlers, emitter and view +``` + +In another terminal, start the processor: + +```sh +go run cmd/processor/main.go -collector # start collector processor +``` + +After you started both Go programs, you can use `curl` or a browser to see messages sent to users, for example, [http://localhost:8080/Alice/feed](http://localhost:8080/Alice/feed). +You can send messages using `curl`, for example, + +```bash +curl -X POST \ + -d '{"to": "Alice", "content": "Hey, how are you doing?"}' \ + http://localhost:8080/Bob/send +``` + +To simplify the use of this example, we also have a load generator, which periodically +generates messages and sends them calling the send endpoint. +To start it, type the following in a third terminal: + +```sh +go run cmd/loadgen/main.go +``` + +## 2. Blocking users + +After running the example with the load generator for a while, one can recognize that Alice and other users receive quite a few messages from Bob. +Bob is actually a spammer and should be blocked! + +### Blocker processor + +For that we create a [blocker processor](blocker/blocker.go#L44), which keeps a table of users that have been blocked. +The blocker processor consumes from `blocker.Stream` and stores a `BlockValue` in the `blocker.Table`: + +```go +func block(ctx goka.Context, msg interface{}) { + var s *BlockValue + if v := ctx.Value(); v == nil { + s = new(BlockValue) + } else { + s = v.(*BlockValue) + } + + if msg.(*BlockEvent).Unblock { + s.Blocked = false + } else { + s.Blocked = true + } + ctx.SetValue(s) +} +``` + +To add or remove a user from the blocker table, we can use the command line tool cmd/block-user: + +```sh +go run cmd/block-user/main.go -user Bob # use -unblock to unblock the user +``` + +### `Context.Join()`: filter out messages from blocked users + +Of course, just adding Bob into `blocker.Table` does not yet guarantee users do not receive messages from him. +For that we need to add a filter between the send endpoint and the collector, which drops messages from blocked users before forwarding them to `ReceivedStream`. + +Our block processor already keeps a table with a block flag for blocked keys, i.e., blocked users. +So, we change the emitter such that it emits into `SentStream`, with the key being now the *sender* instead of the recipient. +Next, we introduce a filter processor that consumes from `SentStream` and only if the key is not blocked, it emits the message into `ReceivedStream`. +The filter is stateless, i.e., it updates no group table. +See the following figure for the resulting architecture. + +![Architecture 2](figs/goka-arch-blocker.png) + +The [filter processor](filter/filter.go#L21) fetches the `BlockValue` for the sender by calling `ctx.Join(blocker.Table)`. +If such value exists and the flag is set to true, then the sender is blocked and the message has to be dropped. +Otherwise, the message is forwarded to `ReceivedStream` with the *recipient* as key. + +```go +func filter(ctx goka.Context, msg interface{}) { + v := ctx.Join(blocker.Table) + if v != nil && v.(*blocker.BlockValue).Blocked { + return + } + ctx.Emit(messaging.ReceivedStream, m.To, m) +} +``` + +The group graph of the filter processor has to be created with an edge to `blocker.Table`: +```go +g := goka.DefineGroup(goka.Group("filter"), + goka.Input(messaging.SentStream, new(messaging.MessageCodec), filter), + goka.Output(messaging.ReceivedStream, new(messaging.MessageCodec)), + goka.Join(blocker.Table, new(blocker.BlockValueCodec)), +) + +p, _ := goka.NewProcessor(brokers, g) +_ = p.Start() + +``` + +Nothing has to be changed in the collector processor or in the feed endpoint. + +### Restarting the example + +To enable the blocker and filter processors, stop `cmd/processor` and restart it as follows: +```sh +go run cmd/processor/main.go -collector -blocker -filter +``` + +Internally the Go Program will start three Goka processors. +Alternatively, you can run the processors individually by starting the program multiple times with the respective flags. + +We still need to inform the send endpoint to emit into `SentStream`. +For that, restart the process with `-sent` flag: + +```sh +go run cmd/service/main.go -sent +``` + +After blocking Bob with `cmd/block-user`, we can see that quickly Bob disappears from the last received messages of Alice. + +## 3. Adding some l33t speak + +To make our example a bit more interesting and introduce the concept of lookup tables, we extend the filter processor to additionally rewrite the content of messages with [l33tspeak](https://en.wikipedia.org/wiki/Leet). +Before extending the filter, though, we create the translator processor that keeps a mapping from word to translation in a similar fashion as the blocker processor. +Note that, differently from `blocker.Table`, the `translator.Table` has words as keys instead of users. + +The [`cmd/translate-word`](cmd/translate-word/main.go) command allows us to add word translations into the table. +Here are some l33tspeak examples: + +```sh +go run cmd/translate-word/main.go -word "together" -with "t°9e+her" +go run cmd/translate-word/main.go -word "lunch" -with "1[_]n<)-(" +go run cmd/translate-word/main.go -word "Hello" -with "H£1|_°" +``` + +### `Context.Lookup()`: querying non-copartitioned tables + +The keys in the `translator.Table` are words instead of users, so the filter processor cannot join the table with the `SentStream` based on the keys. +Instead, we should extend add a `Lookup()` edge to the group graph when creating the filter processor as follows: + +```go +g := goka.DefineGroup(goka.Group("filter"), + goka.Lookup(translator.Table, new(translator.ValueCodec)), + ... +) +``` + +We extend the filter callback to drop blocked users and translate messages. + +```go +func filter(ctx goka.Context, msg interface{}) { + if shouldDrop(ctx) { + return + } + m := translate(ctx, msg.(*messaging.Message)) + ctx.Emit(messaging.ReceivedStream, m.To, m) +} + +func shouldDrop(ctx goka.Context) bool { + // Join() returns the value for ctx.Key() in blocker.Table + v := ctx.Join(blocker.Table) + return v != nil && v.(*blocker.BlockValue).Blocked +} + +func translate(ctx goka.Context, m *messaging.Message) *messaging.Message { + words := strings.Split(m.Content, " ") + for i, w := range words { + // Lookup() returns the value for key w in translator.Table + if tw := ctx.Lookup(translator.Table, w); tw != nil { + words[i] = tw.(string) + } + } + return &messaging.Message{ + From: m.From, + To: m.To, + Content: strings.Join(words, " "), + } +} +``` + +The upside of lookup tables is that they can be queried by any key inside the processor callback. +The downside is that if we spawn multiple processor instances, i.e., if we partition the load among multiple program instances, the complete lookup table has to be kept in each of these instances because we cannot know beforehand which table keys will be queried in which stream partitions. +In contrast, joined tables are copartitioned with the input streams and the group table and, consequently, only the partitions served by each processor instance have to be kept up-to-date. + +### Running example + +Start `cmd/processor` with `-translator` flag and translate words using `cmd/translate-word`. +No further changes are necessary. + +## 4. Automatic spammer detection + +Our final step in this example is to block spammers automatically. +Let's build -- a rather naive -- spammer detector. + +### Detecting spammers +Assume that spammers have the property of sending many more messages than receiving. +So, if we can detect users that fulfill that property, we can block them. + +We want to build a [detector processor](detector/detector.go#L48) that counts how many messages a user sends and receives and issues a `BlockEvent` if the ratio *sent/(sent+received)* exceeds a threshold. +The detector table should keep the following value for each user. +```go +type Counters struct { + Sent int + Received int +} +``` + +Whenever the table value is updated, it should check whether the user is a spammer. +If the number of messages sent is higher than `minMessages` and the sent rate is higher than some `maxRate`, we declare the user to be a spammer and issue a `BlockEvent`. + +```go +func detectSpammer(ctx goka.Context, c *Counters) { + var ( + total = float64(c.Sent + c.Received) + rate = float64(c.Sent) / total + ) + if total >= minMessages && rate >= maxRate { + ctx.Emit(blocker.Stream, ctx.Key(), new(blocker.BlockEvent)) + } +} +``` + +Note that detecting spammers is much more complicated. +Watch [this video](https://tech.lovoo.com/2017/06/16/bbuzz-17-anti-spam-and-machine-learning-at-lovoo/) +for details. + +### `Context.Loopback()`: Counting sent and received messages with one processor + +Now, we defined an approach to detect spammers, but we have to keep the values in the group table updated. +We define the group graph in parts. +We start with the callback for `SentStream`: + +```go +input := goka.Input(messaging.SentStream, new(messaging.MessageCodec), + func(ctx goka.Context, msg interface{}) { + c := getValue(ctx) + c.Sent++ + ctx.SetValue(c) + m := msg.(*messaging.Message) + ctx.Loopback(m.From, m) + detectSpammer(ctx, c) + }, +) + +func getValue(ctx goka.Context) *Counters { + if v := ctx.Value(); v != nil { + return v.(*Counters) + } + return &Counters{} +} +``` + +For every message received from `SentStream`, we first get the value for the key or create a new `Counters` object. +`SentStream` has the sender as key, so we increment `c.Sent` and store back in the group table with `ctx.SetValue()`. +Next, we call `detectSpammer(ctx, c)`, which will check whether sent rate is higher than a threshold. +Finally, we forward the message to the key of the recipient of the message using `ctx.Loopback()`. + + +`ctx.Loopback()` writes in a special topic which is consumed by the loop callback. +If we have multiple instances of the detector sharing the load, the loop callback may even be handled in another instance than the one that called `ctx.Loopback()`. +We define the callback as follows: +```go +loop := goka.Loop(new(messaging.MessageCodec), + func(ctx goka.Context, msg interface{}) { + c := getValue(ctx) + c.Received++ + ctx.SetValue(c) + detectSpammer(ctx, c) + }, +) + +``` +Here again, we first get the value for the key. +Since the key is now the receiver user, we increment `c.Received` and update that value in the group table. +Next, we check whether the user is a spammer with the following function. + +### Group graph +Finally, we define the complete group as follows: +```go +g := goka.DefineGroup(goka.Group("detector"), + input, + loop, + goka.Output(blocker.Stream, new(blocker.BlockEventCodec)), + goka.Persist(new(CountersCodec)), +) + +p, _ := goka.NewProcessor(brokers, g) +_ = p.Start() +``` + +### Running the example + +Start `cmd/processor` with `-detector` flag and unblock Bob. +He should be quickly blocked again. + diff --git a/examples/3-messaging/blocker/blocker.go b/examples/3-messaging/blocker/blocker.go new file mode 100644 index 00000000..8150165f --- /dev/null +++ b/examples/3-messaging/blocker/blocker.go @@ -0,0 +1,70 @@ +package blocker + +import ( + "encoding/json" + + "github.com/lovoo/goka" +) + +var ( + group goka.Group = "blocker" + Table goka.Table = goka.GroupTable(group) + Stream goka.Stream = "block_user" +) + +type BlockEvent struct { + Unblock bool +} + +type BlockEventCodec struct{} + +func (c *BlockEventCodec) Encode(value interface{}) ([]byte, error) { + return json.Marshal(value) +} + +func (c *BlockEventCodec) Decode(data []byte) (interface{}, error) { + var m BlockEvent + return &m, json.Unmarshal(data, &m) +} + +type BlockValue struct { + Blocked bool +} +type BlockValueCodec struct{} + +func (c *BlockValueCodec) Encode(value interface{}) ([]byte, error) { + return json.Marshal(value) +} + +func (c *BlockValueCodec) Decode(data []byte) (interface{}, error) { + var m BlockValue + return &m, json.Unmarshal(data, &m) +} + +func block(ctx goka.Context, msg interface{}) { + var s *BlockValue + if v := ctx.Value(); v == nil { + s = new(BlockValue) + } else { + s = v.(*BlockValue) + } + + if msg.(*BlockEvent).Unblock { + s.Blocked = false + } else { + s.Blocked = true + } + ctx.SetValue(s) +} + +func Run(brokers []string) { + g := goka.DefineGroup(group, + goka.Input(Stream, new(BlockEventCodec), block), + goka.Persist(new(BlockValueCodec)), + ) + if p, err := goka.NewProcessor(brokers, g); err != nil { + panic(err) + } else if err = p.Start(); err != nil { + panic(err) + } +} diff --git a/examples/3-messaging/cmd/block-user/main.go b/examples/3-messaging/cmd/block-user/main.go new file mode 100644 index 00000000..bf563818 --- /dev/null +++ b/examples/3-messaging/cmd/block-user/main.go @@ -0,0 +1,34 @@ +package main + +import ( + "flag" + "fmt" + "os" + + "github.com/lovoo/goka" + "github.com/lovoo/goka/examples/3-messaging/blocker" +) + +var ( + user = flag.String("user", "", "user to block") + unblock = flag.Bool("unblock", false, "unblock user instead of blocking") + broker = flag.String("broker", "localhost:9092", "boostrap Kafka broker") +) + +func main() { + flag.Parse() + if *user == "" { + fmt.Println("cannot block user ''") + os.Exit(1) + } + emitter, err := goka.NewEmitter([]string{*broker}, blocker.Stream, new(blocker.BlockEventCodec)) + if err != nil { + panic(err) + } + defer emitter.Finish() + + err = emitter.EmitSync(*user, &blocker.BlockEvent{Unblock: *unblock}) + if err != nil { + panic(err) + } +} diff --git a/examples/3-messaging/cmd/loadgen/main.go b/examples/3-messaging/cmd/loadgen/main.go new file mode 100644 index 00000000..0ddf4a6a --- /dev/null +++ b/examples/3-messaging/cmd/loadgen/main.go @@ -0,0 +1,86 @@ +package main + +import ( + "bytes" + "encoding/json" + "fmt" + "log" + "math/rand" + "net/http" + "time" + + "github.com/lovoo/goka/examples/3-messaging" +) + +const ( + urlTmpl = "http://localhost:8080/%s/send" + spamProb = 0.3 +) + +var ( + users = []string{ + "Alice", + "Bob", + "Charlie", + "Dave", + "Eve", + } + + contents = []string{ + "Hi how are you doing", + "Hello let's have lunch together", + "Bye", + } +) + +func send(from, to, content string) { + m := messaging.Message{ + To: to, + Content: content, + } + + b, err := json.Marshal(&m) + if err != nil { + log.Printf("error encoding message: %v", err) + return + } + + url := fmt.Sprintf(urlTmpl, from) + + req, err := http.NewRequest("POST", url, bytes.NewBuffer(b)) + if err != nil { + log.Printf("error creating request: %v", err) + return + } + req.Header.Set("Content-Type", "application/json") + + client := &http.Client{} + resp, err := client.Do(req) + if err != nil { + log.Printf("error sending request: %v", err) + return + } + defer resp.Body.Close() + //TODO(diogo) check response status code +} + +func main() { + t := time.NewTicker(200 * time.Millisecond) + defer t.Stop() + + for range t.C { + var ( + cnt = "spam!" + from = "Bob" + ) + if rand.Float64() < 1-spamProb { + from = users[rand.Intn(len(users))] + cnt = contents[rand.Intn(len(contents))] + } + to := users[rand.Intn(len(users))] + for to == from { + to = users[rand.Intn(len(users))] + } + send(from, to, cnt) + } +} diff --git a/examples/3-messaging/cmd/processor/main.go b/examples/3-messaging/cmd/processor/main.go new file mode 100644 index 00000000..e78cf491 --- /dev/null +++ b/examples/3-messaging/cmd/processor/main.go @@ -0,0 +1,58 @@ +package main + +import ( + "flag" + "log" + "os" + "os/signal" + "syscall" + + "github.com/lovoo/goka/examples/3-messaging/blocker" + "github.com/lovoo/goka/examples/3-messaging/collector" + "github.com/lovoo/goka/examples/3-messaging/detector" + "github.com/lovoo/goka/examples/3-messaging/filter" + "github.com/lovoo/goka/examples/3-messaging/translator" +) + +var ( + brokers = []string{"localhost:9092"} + runFilter = flag.Bool("filter", false, "run filter processor") + runCollector = flag.Bool("collector", false, "run collector processor") + runTranslator = flag.Bool("translator", false, "run translator processor") + runBlocker = flag.Bool("blocker", false, "run blocker processor") + runDetector = flag.Bool("detector", false, "run detector processor") + broker = flag.String("broker", "localhost:9092", "boostrap Kafka broker") +) + +func main() { + flag.Parse() + if *runCollector { + log.Println("starting collector") + go collector.Run(brokers) + } + if *runFilter { + log.Println("starting filter") + go filter.Run(brokers) + } + if *runBlocker { + log.Println("starting blocker") + go blocker.Run(brokers) + } + if *runDetector { + log.Println("starting detector") + go detector.Run(brokers) + } + if *runTranslator { + log.Println("starting translator") + go translator.Run(brokers) + } + + // Wait for SIGINT/SIGTERM + waiter := make(chan os.Signal, 1) + signal.Notify(waiter, syscall.SIGINT, syscall.SIGTERM) + + select { + case signal := <-waiter: + log.Printf("Got interrupted by %v", signal) + } +} diff --git a/examples/3-messaging/cmd/service/main.go b/examples/3-messaging/cmd/service/main.go new file mode 100644 index 00000000..3a19c7ee --- /dev/null +++ b/examples/3-messaging/cmd/service/main.go @@ -0,0 +1,22 @@ +package main + +import ( + "flag" + + "github.com/lovoo/goka/examples/3-messaging" + "github.com/lovoo/goka/examples/3-messaging/service" +) + +var ( + sent = flag.Bool("sent", false, "emit to SentStream") + broker = flag.String("broker", "localhost:9092", "boostrap Kafka broker") +) + +func main() { + flag.Parse() + if *sent { + service.Run([]string{*broker}, messaging.SentStream) + } else { + service.Run([]string{*broker}, messaging.ReceivedStream) + } +} diff --git a/examples/3-messaging/cmd/translate-word/main.go b/examples/3-messaging/cmd/translate-word/main.go new file mode 100644 index 00000000..8b9e4559 --- /dev/null +++ b/examples/3-messaging/cmd/translate-word/main.go @@ -0,0 +1,35 @@ +package main + +import ( + "flag" + "fmt" + "os" + + "github.com/lovoo/goka" + "github.com/lovoo/goka/codec" + "github.com/lovoo/goka/examples/3-messaging/translator" +) + +var ( + word = flag.String("word", "", "word to translate") + with = flag.String("with", "", "word translation") + broker = flag.String("broker", "localhost:9092", "boostrap Kafka broker") +) + +func main() { + flag.Parse() + if *word == "" { + fmt.Println("cannot translate word ''") + os.Exit(1) + } + emitter, err := goka.NewEmitter([]string{*broker}, translator.Stream, new(codec.String)) + if err != nil { + panic(err) + } + defer emitter.Finish() + + err = emitter.EmitSync(*word, *with) + if err != nil { + panic(err) + } +} diff --git a/examples/3-messaging/collector/collector.go b/examples/3-messaging/collector/collector.go new file mode 100644 index 00000000..93c07d47 --- /dev/null +++ b/examples/3-messaging/collector/collector.go @@ -0,0 +1,54 @@ +package collector + +import ( + "encoding/json" + + "github.com/lovoo/goka" + "github.com/lovoo/goka/examples/3-messaging" +) + +const maxMessages = 5 + +var ( + group goka.Group = "collector" + Table goka.Table = goka.GroupTable(group) +) + +type MessageListCodec struct{} + +func (c *MessageListCodec) Encode(value interface{}) ([]byte, error) { + return json.Marshal(value) +} + +func (c *MessageListCodec) Decode(data []byte) (interface{}, error) { + var m []messaging.Message + err := json.Unmarshal(data, &m) + return m, err +} + +func collect(ctx goka.Context, msg interface{}) { + var ml []messaging.Message + if v := ctx.Value(); v != nil { + ml = v.([]messaging.Message) + } + + m := msg.(*messaging.Message) + ml = append(ml, *m) + + if len(ml) > maxMessages { + ml = ml[len(ml)-maxMessages:] + } + ctx.SetValue(ml) +} + +func Run(brokers []string) { + g := goka.DefineGroup(group, + goka.Input(messaging.ReceivedStream, new(messaging.MessageCodec), collect), + goka.Persist(new(MessageListCodec)), + ) + if p, err := goka.NewProcessor(brokers, g); err != nil { + panic(err) + } else if err = p.Start(); err != nil { + panic(err) + } +} diff --git a/examples/3-messaging/detector/detector.go b/examples/3-messaging/detector/detector.go new file mode 100644 index 00000000..5ebda4da --- /dev/null +++ b/examples/3-messaging/detector/detector.go @@ -0,0 +1,80 @@ +package detector + +import ( + "encoding/json" + + "github.com/lovoo/goka" + "github.com/lovoo/goka/examples/3-messaging" + "github.com/lovoo/goka/examples/3-messaging/blocker" +) + +const ( + minMessages = 1000 + maxRate = 0.90 +) + +var ( + group goka.Group = "detector" +) + +type Counters struct { + Sent int + Received int +} + +type CountersCodec struct{} + +func (c *CountersCodec) Encode(value interface{}) ([]byte, error) { + return json.Marshal(value) +} + +func (c *CountersCodec) Decode(data []byte) (interface{}, error) { + var m Counters + return &m, json.Unmarshal(data, &m) +} + +func getValue(ctx goka.Context) *Counters { + if v := ctx.Value(); v != nil { + return v.(*Counters) + } + return &Counters{} +} + +func loopToSender(ctx goka.Context, msg interface{}) { + m := msg.(*messaging.Message) + ctx.Loopback(m.From, m) +} + +func detectSpammer(ctx goka.Context, c *Counters) { + var ( + total = float64(c.Sent + c.Received) + rate = float64(c.Sent) / total + ) + if total >= minMessages && rate >= maxRate { + ctx.Emit(blocker.Stream, ctx.Key(), new(blocker.BlockEvent)) + } +} + +func Run(brokers []string) { + g := goka.DefineGroup(group, + goka.Input(messaging.SentStream, new(messaging.MessageCodec), func(ctx goka.Context, msg interface{}) { + c := getValue(ctx) + c.Received++ + ctx.SetValue(c) + loopToSender(ctx, msg) + }), + goka.Loop(new(messaging.MessageCodec), func(ctx goka.Context, msg interface{}) { + c := getValue(ctx) + c.Sent++ + ctx.SetValue(c) + detectSpammer(ctx, c) + }), + goka.Output(blocker.Stream, new(blocker.BlockEventCodec)), + goka.Persist(new(CountersCodec)), + ) + if p, err := goka.NewProcessor(brokers, g); err != nil { + panic(err) + } else if err = p.Start(); err != nil { + panic(err) + } +} diff --git a/examples/3-messaging/figs/goka-arch-blocker.png b/examples/3-messaging/figs/goka-arch-blocker.png new file mode 100644 index 0000000000000000000000000000000000000000..2b7b2155e24c704a2b83334c47de00fc49058b5f GIT binary patch literal 99066 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 literal 0 HcmV?d00001 diff --git a/examples/3-messaging/figs/goka-arch-simple.png b/examples/3-messaging/figs/goka-arch-simple.png new file mode 100644 index 0000000000000000000000000000000000000000..2346dc7ee45f98d593999ed59d7d06c4a495ba9a GIT binary patch literal 68302 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 literal 0 HcmV?d00001 diff --git a/examples/3-messaging/filter/filter.go b/examples/3-messaging/filter/filter.go new file mode 100644 index 00000000..73cbd590 --- /dev/null +++ b/examples/3-messaging/filter/filter.go @@ -0,0 +1,55 @@ +package filter + +import ( + "strings" + + "github.com/lovoo/goka" + messaging "github.com/lovoo/goka/examples/3-messaging" + "github.com/lovoo/goka/examples/3-messaging/blocker" + "github.com/lovoo/goka/examples/3-messaging/translator" +) + +var ( + group goka.Group = "message_filter" +) + +func shouldDrop(ctx goka.Context) bool { + v := ctx.Join(blocker.Table) + return v != nil && v.(*blocker.BlockValue).Blocked +} + +func filter(ctx goka.Context, msg interface{}) { + if shouldDrop(ctx) { + return + } + m := translate(ctx, msg.(*messaging.Message)) + ctx.Emit(messaging.ReceivedStream, m.To, m) +} + +func translate(ctx goka.Context, m *messaging.Message) *messaging.Message { + words := strings.Split(m.Content, " ") + for i, w := range words { + if tw := ctx.Lookup(translator.Table, w); tw != nil { + words[i] = tw.(string) + } + } + return &messaging.Message{ + From: m.From, + To: m.To, + Content: strings.Join(words, " "), + } +} + +func Run(brokers []string) { + g := goka.DefineGroup(group, + goka.Input(messaging.SentStream, new(messaging.MessageCodec), filter), + goka.Output(messaging.ReceivedStream, new(messaging.MessageCodec)), + goka.Join(blocker.Table, new(blocker.BlockValueCodec)), + goka.Lookup(translator.Table, new(translator.ValueCodec)), + ) + if p, err := goka.NewProcessor(brokers, g); err != nil { + panic(err) + } else if err = p.Start(); err != nil { + panic(err) + } +} diff --git a/examples/3-messaging/message.go b/examples/3-messaging/message.go new file mode 100644 index 00000000..17c61a5f --- /dev/null +++ b/examples/3-messaging/message.go @@ -0,0 +1,41 @@ +package messaging + +import ( + "encoding/json" + + "github.com/lovoo/goka" +) + +var ( + SentStream goka.Stream = "message_sent" + ReceivedStream goka.Stream = "message_received" +) + +type Message struct { + From string + To string + Content string +} + +type MessageCodec struct{} + +func (c *MessageCodec) Encode(value interface{}) ([]byte, error) { + return json.Marshal(value) +} + +func (c *MessageCodec) Decode(data []byte) (interface{}, error) { + var m Message + return &m, json.Unmarshal(data, &m) +} + +type MessageListCodec struct{} + +func (c *MessageListCodec) Encode(value interface{}) ([]byte, error) { + return json.Marshal(value) +} + +func (c *MessageListCodec) Decode(data []byte) (interface{}, error) { + var m []Message + err := json.Unmarshal(data, &m) + return m, err +} diff --git a/examples/3-messaging/service/service.go b/examples/3-messaging/service/service.go new file mode 100644 index 00000000..3f372ec4 --- /dev/null +++ b/examples/3-messaging/service/service.go @@ -0,0 +1,84 @@ +package service + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "log" + "net/http" + + "github.com/gorilla/mux" + "github.com/lovoo/goka" + "github.com/lovoo/goka/examples/3-messaging" + "github.com/lovoo/goka/examples/3-messaging/collector" +) + +func Run(brokers []string, stream goka.Stream) { + view, err := goka.NewView(brokers, collector.Table, new(collector.MessageListCodec)) + if err != nil { + panic(err) + } + go view.Start() + defer view.Stop() + + emitter, err := goka.NewEmitter(brokers, stream, new(messaging.MessageCodec)) + if err != nil { + panic(err) + } + defer emitter.Finish() + + router := mux.NewRouter() + router.HandleFunc("/{user}/send", send(emitter, stream)).Methods("POST") + router.HandleFunc("/{user}/feed", feed(view)).Methods("GET") + + log.Printf("Listen port 8080") + log.Fatal(http.ListenAndServe(":8080", router)) +} + +func send(emitter *goka.Emitter, stream goka.Stream) func(w http.ResponseWriter, r *http.Request) { + return func(w http.ResponseWriter, r *http.Request) { + var m messaging.Message + + b, err := ioutil.ReadAll(r.Body) + if err != nil { + fmt.Fprintf(w, "error: %v", err) + return + } + + err = json.Unmarshal(b, &m) + if err != nil { + fmt.Fprintf(w, "error: %v", err) + return + } + + m.From = mux.Vars(r)["user"] + + if stream == messaging.ReceivedStream { + err = emitter.EmitSync(m.To, &m) + } else { + err = emitter.EmitSync(m.From, &m) + } + if err != nil { + fmt.Fprintf(w, "error: %v", err) + return + } + log.Printf("Sent message:\n %v\n", m) + fmt.Fprintf(w, "Sent message:\n %v\n", m) + } +} + +func feed(view *goka.View) func(w http.ResponseWriter, r *http.Request) { + return func(w http.ResponseWriter, r *http.Request) { + user := mux.Vars(r)["user"] + val, _ := view.Get(user) + if val == nil { + fmt.Fprintf(w, "%s not found!", user) + return + } + messages := val.([]messaging.Message) + fmt.Fprintf(w, "Latest messages for %s\n", user) + for i, m := range messages { + fmt.Fprintf(w, "%d %10s: %v\n", i, m.From, m.Content) + } + } +} diff --git a/examples/3-messaging/translator/translator.go b/examples/3-messaging/translator/translator.go new file mode 100644 index 00000000..a1f0efad --- /dev/null +++ b/examples/3-messaging/translator/translator.go @@ -0,0 +1,32 @@ +package translator + +import ( + "github.com/lovoo/goka" + "github.com/lovoo/goka/codec" +) + +var ( + group goka.Group = "translator" + Table goka.Table = goka.GroupTable(group) + Stream goka.Stream = "translate-word" +) + +type ValueCodec struct { + codec.String +} + +func translate(ctx goka.Context, msg interface{}) { + ctx.SetValue(msg.(string)) +} + +func Run(brokers []string) { + g := goka.DefineGroup(group, + goka.Input(Stream, new(ValueCodec), translate), + goka.Persist(new(ValueCodec)), + ) + if p, err := goka.NewProcessor(brokers, g); err != nil { + panic(err) + } else if err = p.Start(); err != nil { + panic(err) + } +} From 4e06cb40fbeea0e49156763e9b030f1107c430ba Mon Sep 17 00:00:00 2001 From: Stefan Weigert Date: Wed, 7 Feb 2018 15:24:56 +0100 Subject: [PATCH 2/5] fixing detector --- examples/3-messaging/detector/detector.go | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/examples/3-messaging/detector/detector.go b/examples/3-messaging/detector/detector.go index 5ebda4da..6c1fbf4e 100644 --- a/examples/3-messaging/detector/detector.go +++ b/examples/3-messaging/detector/detector.go @@ -9,8 +9,8 @@ import ( ) const ( - minMessages = 1000 - maxRate = 0.90 + minMessages = 200 + maxRate = 0.5 ) var ( @@ -40,11 +40,6 @@ func getValue(ctx goka.Context) *Counters { return &Counters{} } -func loopToSender(ctx goka.Context, msg interface{}) { - m := msg.(*messaging.Message) - ctx.Loopback(m.From, m) -} - func detectSpammer(ctx goka.Context, c *Counters) { var ( total = float64(c.Sent + c.Received) @@ -59,14 +54,19 @@ func Run(brokers []string) { g := goka.DefineGroup(group, goka.Input(messaging.SentStream, new(messaging.MessageCodec), func(ctx goka.Context, msg interface{}) { c := getValue(ctx) - c.Received++ + c.Sent++ ctx.SetValue(c) - loopToSender(ctx, msg) + m := msg.(*messaging.Message) + // Loop to receiver + ctx.Loopback(m.To, m) + // Check if sender is a spammer + detectSpammer(ctx, c) }), goka.Loop(new(messaging.MessageCodec), func(ctx goka.Context, msg interface{}) { c := getValue(ctx) - c.Sent++ + c.Received++ ctx.SetValue(c) + // Check if receiver is a spammer detectSpammer(ctx, c) }), goka.Output(blocker.Stream, new(blocker.BlockEventCodec)), From 38669f779245164b4d2a2bb2cad25a91c52cda5c Mon Sep 17 00:00:00 2001 From: Diogo Behrens Date: Wed, 7 Feb 2018 16:14:46 +0100 Subject: [PATCH 3/5] make detector return bool --- examples/3-messaging/README.md | 94 +++++++++++++++++------ examples/3-messaging/detector/detector.go | 23 +++--- 2 files changed, 83 insertions(+), 34 deletions(-) diff --git a/examples/3-messaging/README.md b/examples/3-messaging/README.md index c2d5bee9..6e9b2aad 100644 --- a/examples/3-messaging/README.md +++ b/examples/3-messaging/README.md @@ -43,7 +43,7 @@ type Message struct { If Bob wants to send a message to Alice, he would send a request to the send endpoint with the recipient and the content of the message. For example: -```bash +```sh curl -X POST \ -d '{"to": "Alice", "content": "Hey, how are you doing?"}' \ http://localhost:8080/Bob/send @@ -77,13 +77,15 @@ router.HandleFunc("/{user}/send", send(emitter)).Methods("POST") Note we are ignoring errors for the sake of readability. The complete example in the repository handles them, though. -### `Context.Value()` and `Context.SetValue()`: collecting messages +### Collecting messages with `Context.Value()` and `Context.SetValue()` We define the *collector table* to contain the latest 5 messages received by each user. The *collector processor* keeps the table up-to-date by consuming `ReceivedStream`. The collector callback is defined as [follows](collector/collector.go#L29): ```go +// collect callback is called for every message from ReceivedStream. +// ctx allows access to collector table and msg is the input message. func collect(ctx goka.Context, msg interface{}) { var ml []messaging.Message if v := ctx.Value(); v != nil { @@ -98,14 +100,29 @@ func collect(ctx goka.Context, msg interface{}) { } ctx.SetValue(ml) } + ``` -The `ctx` is scoped with the key of the message -- we used the receiver as key in the emitter. -With `ctx.Value()` we fetch the table value for the key. -The value is a slice of messages. +The `ctx` is scoped with the key of the input message -- remember we used the receiver as key in the emitter. +With `ctx.Value()` we fetch the table value for that key. +In this processor, the value is a slice of messages. We then append the received message and cap the length of the slice with the constant `maxMessages`, which is 5. Finally, we store the value back in the table with `ctx.SetValue()`. + +To create the processor, we need to define the group input stream and table persistency: +```go +g := goka.DefineGroup(goka.Group("collector"), + // the group table ("collector-table") persists message lists + goka.Persist(new(MessageListCodec)), + // input stream is ReceivedStream with MessageCodec and collect callback + goka.Input(messaging.ReceivedStream, new(messaging.MessageCodec), collect), +) +p, _ := goka.NewProcessor(brokers, g) +go p.Start() + +``` + ### Feed endpoint When Alice wants to see her 5 latest received messages, she requests that from the feed endpoint. @@ -117,7 +134,7 @@ Latest messages for Alice 1 Charlie: See you later. ``` -The handler employs a view on `collector.Table` to retrieve the messages for alice. +The handler employs a view on `collector.Table` to retrieve the messages for Alice. It gets the user from the URL and tries to get the value from the view. If no value is available, the user has received no messages yet. Otherwise, the handler loops over the messages in the slice and formats the output. @@ -163,7 +180,8 @@ In this example, we put the endpoint handlers and, consequently, emitter and vie In another Go program, we start the collector processor. This solution allows us to start, stop, and scale them independently. -To start the service in a terminal, change directory to `examples/3-messaging` and type: +Before starting any Go program, run `make start` in `examples` to start Docker containers for ZooKeeper and Kafka. +Next, to start the service, change directory to `examples/3-messaging` and type: ```sh go run cmd/service/main.go # start endpoint handlers, emitter and view @@ -175,10 +193,17 @@ In another terminal, start the processor: go run cmd/processor/main.go -collector # start collector processor ``` -After you started both Go programs, you can use `curl` or a browser to see messages sent to users, for example, [http://localhost:8080/Alice/feed](http://localhost:8080/Alice/feed). +After you started both Go programs, you can use `curl` to see the messages sent to Alice: + +```sh +curl localhost:8080/Alice/feed +``` + +or open [http://localhost:8080/Alice/feed](http://localhost:8080/Alice/feed) in the browser. + You can send messages using `curl`, for example, -```bash +```sh curl -X POST \ -d '{"to": "Alice", "content": "Hey, how are you doing?"}' \ http://localhost:8080/Bob/send @@ -226,7 +251,7 @@ To add or remove a user from the blocker table, we can use the command line tool go run cmd/block-user/main.go -user Bob # use -unblock to unblock the user ``` -### `Context.Join()`: filter out messages from blocked users +### Filter messages from blocked users with `Context.Join()` Of course, just adding Bob into `blocker.Table` does not yet guarantee users do not receive messages from him. For that we need to add a filter between the send endpoint and the collector, which drops messages from blocked users before forwarding them to `ReceivedStream`. @@ -245,6 +270,8 @@ Otherwise, the message is forwarded to `ReceivedStream` with the *recipient* as ```go func filter(ctx goka.Context, msg interface{}) { + // messaging.SentStream and blocker.Table are copartitioned; + // ctx.Join() gets the value in blocker.Table for key given in ctx.Key() v := ctx.Join(blocker.Table) if v != nil && v.(*blocker.BlockValue).Blocked { return @@ -270,6 +297,15 @@ Nothing has to be changed in the collector processor or in the feed endpoint. ### Restarting the example +At this point, let's make a short recap. So far we have created: + +- a [service](service/service.go) with send and feed endpoints; +- a [collector processor](collector/collector.go) to collect messages sent to users; +- a [block processor](blocker/blocker.go) to keep a table tracking blocked users; +- a [filter processor](filter/filter.go) to drop messages from blocked users before they reach the collector processor; and +- a [block-user tool](cmd/block-user) to add users to the block table. + + To enable the blocker and filter processors, stop `cmd/processor` and restart it as follows: ```sh go run cmd/processor/main.go -collector -blocker -filter @@ -302,7 +338,7 @@ go run cmd/translate-word/main.go -word "lunch" -with "1[_]n<)-(" go run cmd/translate-word/main.go -word "Hello" -with "H£1|_°" ``` -### `Context.Lookup()`: querying non-copartitioned tables +### Querying non-copartitioned tables with `Context.Lookup()` The keys in the `translator.Table` are words instead of users, so the filter processor cannot join the table with the `SentStream` based on the keys. Instead, we should extend add a `Lookup()` edge to the group graph when creating the filter processor as follows: @@ -353,6 +389,11 @@ In contrast, joined tables are copartitioned with the input streams and the grou ### Running example +In step three, we have changed and added some components: + +- added a [translator processor](translator/translator.go) to keep translations of words to l33tspeak; and +- changed the [filter processor](filter/filter.go) to not only drop messages from blocked users but also rewrite messages with l33t translations + Start `cmd/processor` with `-translator` flag and translate words using `cmd/translate-word`. No further changes are necessary. @@ -378,22 +419,16 @@ Whenever the table value is updated, it should check whether the user is a spamm If the number of messages sent is higher than `minMessages` and the sent rate is higher than some `maxRate`, we declare the user to be a spammer and issue a `BlockEvent`. ```go -func detectSpammer(ctx goka.Context, c *Counters) { +func detectSpammer(ctx goka.Context, c *Counters) bool { var ( total = float64(c.Sent + c.Received) rate = float64(c.Sent) / total ) - if total >= minMessages && rate >= maxRate { - ctx.Emit(blocker.Stream, ctx.Key(), new(blocker.BlockEvent)) - } + return total >= minMessages && rate >= maxRate } ``` -Note that detecting spammers is much more complicated. -Watch [this video](https://tech.lovoo.com/2017/06/16/bbuzz-17-anti-spam-and-machine-learning-at-lovoo/) -for details. - -### `Context.Loopback()`: Counting sent and received messages with one processor +### Counting sent and received messages with one processor with `Context.Loopback()` Now, we defined an approach to detect spammers, but we have to keep the values in the group table updated. We define the group graph in parts. @@ -401,13 +436,15 @@ We start with the callback for `SentStream`: ```go input := goka.Input(messaging.SentStream, new(messaging.MessageCodec), - func(ctx goka.Context, msg interface{}) { + func(ctx goka.Context, msg interface{}) { c := getValue(ctx) c.Sent++ ctx.SetValue(c) + if detectSpammer(ctx, c) { + ctx.Emit(blocker.Stream, ctx.Key(), new(blocker.BlockEvent)) + } m := msg.(*messaging.Message) - ctx.Loopback(m.From, m) - detectSpammer(ctx, c) + ctx.Loopback(m.To, m) }, ) @@ -434,7 +471,9 @@ loop := goka.Loop(new(messaging.MessageCodec), c := getValue(ctx) c.Received++ ctx.SetValue(c) - detectSpammer(ctx, c) + if detectSpammer(ctx, c) { + ctx.Emit(blocker.Stream, ctx.Key(), new(blocker.BlockEvent)) + } }, ) @@ -459,6 +498,11 @@ _ = p.Start() ### Running the example -Start `cmd/processor` with `-detector` flag and unblock Bob. +In this final step, we added a [spam detector](detector/detector.go) which consumes messages from `SentStream` and emits block events into `blocker.Stream` if the sender or receiver of the message seem to be a spammer. + +To test the detector, start `cmd/processor` with `-detector` flag and unblock Bob. He should be quickly blocked again. +Note that in practice detecting spammers is much more complicated than the naive approach taken here. +Watch [this video](https://tech.lovoo.com/2017/06/16/bbuzz-17-anti-spam-and-machine-learning-at-lovoo/) +for details. diff --git a/examples/3-messaging/detector/detector.go b/examples/3-messaging/detector/detector.go index 6c1fbf4e..0d61c85f 100644 --- a/examples/3-messaging/detector/detector.go +++ b/examples/3-messaging/detector/detector.go @@ -40,14 +40,12 @@ func getValue(ctx goka.Context) *Counters { return &Counters{} } -func detectSpammer(ctx goka.Context, c *Counters) { +func detectSpammer(ctx goka.Context, c *Counters) bool { var ( total = float64(c.Sent + c.Received) rate = float64(c.Sent) / total ) - if total >= minMessages && rate >= maxRate { - ctx.Emit(blocker.Stream, ctx.Key(), new(blocker.BlockEvent)) - } + return total >= minMessages && rate >= maxRate } func Run(brokers []string) { @@ -56,18 +54,25 @@ func Run(brokers []string) { c := getValue(ctx) c.Sent++ ctx.SetValue(c) - m := msg.(*messaging.Message) + + // check if sender is a spammer + if detectSpammer(ctx, c) { + ctx.Emit(blocker.Stream, ctx.Key(), new(blocker.BlockEvent)) + } + // Loop to receiver + m := msg.(*messaging.Message) ctx.Loopback(m.To, m) - // Check if sender is a spammer - detectSpammer(ctx, c) }), goka.Loop(new(messaging.MessageCodec), func(ctx goka.Context, msg interface{}) { c := getValue(ctx) c.Received++ ctx.SetValue(c) - // Check if receiver is a spammer - detectSpammer(ctx, c) + + // check if receiver is a spammer + if detectSpammer(ctx, c) { + ctx.Emit(blocker.Stream, ctx.Key(), new(blocker.BlockEvent)) + } }), goka.Output(blocker.Stream, new(blocker.BlockEventCodec)), goka.Persist(new(CountersCodec)), From a3896e4070da54e2943f39c9f25bf03876602c88 Mon Sep 17 00:00:00 2001 From: Diogo Behrens Date: Sun, 25 Feb 2018 13:48:16 +0100 Subject: [PATCH 4/5] updating README --- README.md | 14 +++++++++----- examples/3-messaging/README.md | 3 +++ examples/create-kafka-commands.sh | 32 +++++++++++++++++++++++++++++++ 3 files changed, 44 insertions(+), 5 deletions(-) create mode 100644 examples/create-kafka-commands.sh diff --git a/README.md b/README.md index dd7d6ec1..d36fc98d 100644 --- a/README.md +++ b/README.md @@ -28,8 +28,9 @@ Goka extends the concept of Kafka consumer groups by binding a state table to th ## Documentation This README provides a brief, high level overview of the ideas behind Goka. +A more detailed introduction of the project can be found in this [blog post](https://tech.lovoo.com/2017/05/23/goka/). -Package API documentation is available at [GoDoc]. +Package API documentation is available at [GoDoc] and the [Wiki](https://github.com/lovoo/goka/wiki/Tips#configuring-log-compaction-for-table-topics) provides several tips for configuring, extending, and deploying Goka applications. ## Installation @@ -45,9 +46,9 @@ Goka relies on Kafka for message passing, fault-tolerant state storage and workl * **Processor** is a set of callback functions that consume and perform state transformations upon delivery of these emitted messages. *Processor groups* are formed of one or more instances of a processor. Goka distributes the partitions of the input topics across all processor instances in a processor group. This enables effortless scaling and fault-tolerance. If a processor instance fails, its partitions and state are reassigned to the remaining healthy members of the processor group. Processors can also emit further messages into Kafka. -* **Group tables** are partitioned key-value tables stored in Kafka that belong to a single processor group. If a processor instance fails, the remaining instances will take over the group table partitions of the failed instance recovering them from Kafka. +* **Group table** is the state of a processor group. It is a partitioned key-value table stored in Kafka that belongs to a single processor group. If a processor instance fails, the remaining instances will take over the group table partitions of the failed instance recovering them from Kafka. -* **Views** are local caches of a processor group's complete group table. Views provide read-only access to the group tables and can be used to provide external services for example through a gRPC interface. +* **Views** are local caches of a complete group table. Views provide read-only access to the group tables and can be used to provide external services for example through a gRPC interface. ## Get Started @@ -55,7 +56,7 @@ Goka relies on Kafka for message passing, fault-tolerant state storage and workl An example Goka application could look like the following. An emitter emits a single message with key "some-key" and value "some-value" into the "example-stream" topic. A processor processes the "example-stream" topic counting the number of messages delivered for "some-key". -The counter is persisted in the "example-group-state" topic. +The counter is persisted in the "example-group-table" topic. To locally start a dockerized Zookeeper and Kafka instances, execute `make start` with the `Makefile` in the [examples] folder. ```go @@ -111,7 +112,7 @@ func runProcessor() { } // Define a new processor group. The group defines all inputs, outputs, and - // serialization formats. The group-table topic is "example-group-state". + // serialization formats. The group-table topic is "example-group-table". g := goka.DefineGroup(group, goka.Input(topic, new(codec.String), cb), goka.Persist(new(codec.Int64)), @@ -139,6 +140,9 @@ func main() { } ``` +Note that tables have to be configured in Kafka with log compaction. +For details check the [Wiki](https://github.com/lovoo/goka/wiki/Tips#configuring-log-compaction-for-table-topics). + ## How to contribute Contributions are always welcome. diff --git a/examples/3-messaging/README.md b/examples/3-messaging/README.md index 6e9b2aad..c099bced 100644 --- a/examples/3-messaging/README.md +++ b/examples/3-messaging/README.md @@ -506,3 +506,6 @@ He should be quickly blocked again. Note that in practice detecting spammers is much more complicated than the naive approach taken here. Watch [this video](https://tech.lovoo.com/2017/06/16/bbuzz-17-anti-spam-and-machine-learning-at-lovoo/) for details. + +Note that tables have to be configured in Kafka with log compaction. +For details check the [Wiki](https://github.com/lovoo/goka/wiki/Tips#configuring-log-compaction-for-table-topics). diff --git a/examples/create-kafka-commands.sh b/examples/create-kafka-commands.sh new file mode 100644 index 00000000..a58c1dfa --- /dev/null +++ b/examples/create-kafka-commands.sh @@ -0,0 +1,32 @@ +#!/bin/bash + +## lists the scripts inside the Kafka container and creates local scripts to call them with docker. + +set -e + +# directory to save the scripts +TARGET=$1 +mkdir -p $TARGET + +# create Kafka scripts +SCRIPTS=$(docker run --rm -it --entrypoint /bin/bash wurstmeister/kafka -c "ls \$KAFKA_HOME/bin/*.sh") +for SCRIPT in $SCRIPTS; do + SCRIPT=$(echo $SCRIPT | tr -d '\r') + FN=$TARGET/$(basename $SCRIPT) + echo creating $FN + cat <<-EOF > $FN + #!/bin/bash + CMD="$SCRIPT \$@" + docker run --net=host --rm -it --entrypoint /bin/bash wurstmeister/kafka -c "\$CMD" +EOF + chmod +x $FN +done + +# create ZooKeeper client scriptt +echo creating $TARGET/zkCli.sh +cat <<-EOF > $TARGET/zkCli.sh + #!/bin/bash + CMD="bin/zkCli.sh \$@" + docker run --net=host --rm -it wurstmeister/zookeeper bash -c "\$CMD" +EOF +chmod +x $TARGET/zkCli.sh From 24af59a03074a0bbe5faa5ae91aad66f813c0f1e Mon Sep 17 00:00:00 2001 From: Diogo Behrens Date: Sun, 25 Feb 2018 13:58:06 +0100 Subject: [PATCH 5/5] rewording state to tables to be consistent with current goka --- examples/1-simplest/main.go | 2 +- examples/2-clicks/README.md | 8 ++++---- kafkamock.go | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/examples/1-simplest/main.go b/examples/1-simplest/main.go index 0d4fde9a..840a3227 100644 --- a/examples/1-simplest/main.go +++ b/examples/1-simplest/main.go @@ -50,7 +50,7 @@ func runProcessor() { } // Define a new processor group. The group defines all inputs, outputs, and - // serialization formats. The group-table topic is "example-group-state". + // serialization formats. The group-table topic is "example-group-table". g := goka.DefineGroup(group, goka.Input(topic, new(codec.String), cb), goka.Persist(new(codec.Int64)), diff --git a/examples/2-clicks/README.md b/examples/2-clicks/README.md index 10dbb7fa..f5cecd11 100644 --- a/examples/2-clicks/README.md +++ b/examples/2-clicks/README.md @@ -6,7 +6,7 @@ This example shows how to: * Write a processor that consumes data from kafka, counting clicks for a user * Write an emitter to push data to kafka -* Writing a view to query the user state +* Writing a view to query the user table To get an introduction into goka, see this [blog post](http://tech.lovoo.com/2017/05/23/goka). @@ -22,7 +22,7 @@ go run main.go This should output something like ``` -2017/05/23 15:09:20 Table mini-group-state has 10 partitions +2017/05/23 15:09:20 Table mini-group-table has 10 partitions 2017/05/23 15:09:20 Processor: started View opened at http://localhost:9095/ 2017/05/23 15:09:20 View: started @@ -95,9 +95,9 @@ The consumer of a topic must use the same codec as the writer, otherwise we'll g unmarshalling will simply fail. * `goka.Persist` makes the processor store its group table persistently using kafka. That means on every -restart (either the same host or somewhere else), the state will be restored. +restart (either the same host or somewhere else), the group table will be restored. This option also makes the processor cache the group table locally using a key-value store. -That avoids holding the full state in memory and a long-running recovery on every restart. +That avoids holding the full group table in memory and a long-running recovery on every restart. To persist the group table, again we need a `Codec` which encodes the user for this case. We want to store objects of type `*user`, so we have to implement our own codec. In our example, diff --git a/kafkamock.go b/kafkamock.go index 7244b396..1ab35edd 100644 --- a/kafkamock.go +++ b/kafkamock.go @@ -379,8 +379,8 @@ func (km *consumerMock) Commit(topic string, partition int32, offset int64) erro return nil } -// AddPartition marks the topic as a state topic. -// The mock has to know the state topic to ignore emit calls (which would never be consumed) +// AddPartition marks the topic as a table topic. +// The mock has to know the group table topic to ignore emit calls (which would never be consumed) func (km *consumerMock) AddPartition(topic string, partition int32, initialOffset int64) { }