Event-driven architectures are relatively simple to understand, and it is generally accepted that good software architectures are decoupled and that microservices should not be coupled or dependent on each other. For example, when we call the function of the microservice go.srv.user-service in the code, we will find the address of the microservice through service discovery and then call it. Our code has direct call interaction with the microservice, which is not completely decoupled.
The source address
- The source address
- Comprehensive micro service project of Airentals
Publish and subscribe
To understand how event-driven architecture can completely decouple code, take a look at the publish and subscribe process of events. Microservice X notifies the messaging system that “X has completed” after completing task X. It does not care about which microservices are listening for this event and the impact of the event after it occurs. If an event occurs in the system, it is easy for other microservices to act accordingly.
For example, when a user-service creates a new user, email-service sends an email to the user indicating that the user has registered successfully, and message-service sends a short message to the website administrator notifying the user that the user has registered.
General implementation
After instantiating the other two micro-service clients in the user-service code, the function is called to send mail and SMS, and the code is highly coupled. The diagram below:
event-driven
In an event-driven architecture, a User-Service only needs to publish a topic “user.created” message to the messaging system, and two other services that subscribe to this topic can know that a user has registered, and when they get the user’s information, they can send an email or a text message. The diagram below:
Code implementation
Publish event Publish
package main
import (
"context"
"github.com/asim/go-micro/v3"
"github.com/asim/go-micro/v3/metadata"
"github.com/asim/go-micro/v3/server"
"github.com/asim/go-micro/v3/util/log"
proto "go-micro-examples/pubsub/proto"
)
// Sub All methods of Sub will be executed when
// a message is received
type Sub struct{}
// Process Method can be of any name
func (s *Sub) Process(ctx context.Context, event *proto.Event) error {
md, _ := metadata.FromContext(ctx)
log.Logf("[pubsub.1] Received event %+v with metadata %+v\n", event, md)
// do something with event
return nil
}
// Alternatively a function can be used
func subEv(ctx context.Context, event *proto.Event) error {
md, _ := metadata.FromContext(ctx)
log.Logf("[pubsub.2] Received event %+v with metadata %+v\n", event, md)
// do something with event
return nil
}
func main(a) {
// create a service
service := micro.NewService(
micro.Name("go.micro.srv.pubsub"),// parse command line
service.Init()
// register subscriber
if err := micro.RegisterSubscriber("example.topic,pubsub.1", service.Server(), new(Sub)); err ! =nil {
log.Fatal(err)
}
// register subscriber with queue, each message is delivered to a unique subscriber
if err := micro.RegisterSubscriber("example.topic.pubsub.2", service.Server(), subEv, server.SubscriberQueue("queue.pubsub")); err ! =nil {
log.Fatal(err)
}
iferr := service.Run(); err ! =nil {
log.Fatal(err)
}
}
Copy the code
The running effect is as follows:
Example.top.pubsub. 1, Example.top.pubsub. 2
Subscribe Event Subscription
If events have subscriptions, then of course there will be subscriptions
package main
import (
"context"
"fmt"
"github.com/asim/go-micro/v3"
"github.com/asim/go-micro/v3/util/log"
"github.com/pborman/uuid"
proto "go-micro-examples/pubsub/proto"
"time"
)
// send events using the publisher
func sendEv(topic string, p micro.Publisher) {
t := time.NewTimer(time.Second)
for _ = range t.C {
// crate new event
ev := &proto.Event{
Id: uuid.NewUUID().String(),
Timestamp: time.Now().Unix(),
Message: fmt.Sprintf("Messaging you all day on %s", topic),
}
log.Logf("publishing %+v\n", ev)
// publish an event
iferr := p.Publish(context.Background(), ev); err ! =nil {
log.Logf("error publishing %v", err)
}
}
}
func main(a) {
// create a service
service := micro.NewService(
micro.Name("go.micro.cli.pubsub"),// parse command line
service.Init()
// create publisher
pub1 := micro.NewEvent("example.topic.pubsub.1", service.Client())
pub2 := micro.NewEvent("example.topic.pubsub.2", service.Client())
// pub to topic 1
go sendEv("example.topic.pubsub.1", pub1)
// pub to topic 2
go sendEv("example.topic.pubsub.2", pub2)
// block forever
select{}}Copy the code
The running effect is as follows:
As you can see from the figure, the subscription to the event succeeded and the invocation succeeded