Files
upstream-watermill/_examples/basic/1-your-first-app/main.go
2026-09-15 21:27:46 +05:00

153 lines
3.5 KiB
Go

package main
import (
"context"
"encoding/json"
"fmt"
"time"
"github.com/ThreeDotsLabs/watermill"
"github.com/ThreeDotsLabs/watermill-kafka/v3/pkg/kafka"
"github.com/ThreeDotsLabs/watermill/message"
"github.com/ThreeDotsLabs/watermill/message/router/middleware"
"github.com/ThreeDotsLabs/watermill/message/router/plugin"
)
var (
brokers = []string{"kafka:9092"}
consumeTopic = "events"
publishTopic = "events-processed"
logger = watermill.NewStdLogger(
true, // debug
false, // trace
)
marshaler = kafka.DefaultMarshaler{}
)
type event struct {
ID int `json:"id"`
}
type processedEvent struct {
ProcessedID int `json:"processed_id"`
Time time.Time `json:"time"`
}
func main() {
publisher := createPublisher()
// Subscriber is created with consumer group handler_1
subscriber := createSubscriber("handler_1")
router, err := message.NewRouter(message.RouterConfig{}, logger)
if err != nil {
panic(err)
}
router.AddPlugin(plugin.SignalsHandler)
router.AddMiddleware(middleware.Recoverer)
// Adding a handler (multiple handlers can be added)
router.AddHandler(
"handler_1", // handler name, must be unique
consumeTopic, // topic from which messages should be consumed
subscriber,
publishTopic, // topic to which messages should be published
publisher,
func(msg *message.Message) ([]*message.Message, error) {
consumedPayload := event{}
err := json.Unmarshal(msg.Payload, &consumedPayload)
if err != nil {
// When a handler returns an error, the default behavior is to send a Nack (negative-acknowledgement).
// The message will be processed again.
//
// You can change the default behaviour by using middlewares, like Retry or PoisonQueue.
// You can also implement your own middleware.
return nil, err
}
fmt.Printf("received event %+v\n", consumedPayload)
newPayload, err := json.Marshal(processedEvent{
ProcessedID: consumedPayload.ID,
Time: time.Now(),
})
if err != nil {
return nil, err
}
newMessage := message.NewMessage(watermill.NewUUID(), newPayload)
return []*message.Message{newMessage}, nil
},
)
// Simulate incoming events in the background
go simulateEvents(publisher)
if err := router.Run(context.Background()); err != nil {
panic(err)
}
}
// createPublisher is a helper function that creates a Publisher, in this case - the Kafka Publisher.
func createPublisher() message.Publisher {
kafkaPublisher, err := kafka.NewPublisher(
kafka.PublisherConfig{
Brokers: brokers,
Marshaler: marshaler,
},
logger,
)
if err != nil {
panic(err)
}
return kafkaPublisher
}
// createSubscriber is a helper function similar to the previous one, but in this case it creates a Subscriber.
func createSubscriber(consumerGroup string) message.Subscriber {
kafkaSubscriber, err := kafka.NewSubscriber(
kafka.SubscriberConfig{
Brokers: brokers,
Unmarshaler: marshaler,
ConsumerGroup: consumerGroup, // every handler will use a separate consumer group
},
logger,
)
if err != nil {
panic(err)
}
return kafkaSubscriber
}
// simulateEvents produces events that will be later consumed.
func simulateEvents(publisher message.Publisher) {
i := 0
for {
e := event{
ID: i,
}
payload, err := json.Marshal(e)
if err != nil {
panic(err)
}
err = publisher.Publish(consumeTopic, message.NewMessage(
watermill.NewUUID(), // internal uuid of the message, useful for debugging
payload,
))
if err != nil {
panic(err)
}
i++
time.Sleep(time.Second)
}
}