Subscribe message

In the first article in this series, we created a sample project which already contains codes relating to message subscription.

First, we define the message processing Handler. The code of ./subscriber/hello.go is as follows:

package subscriber



import (

"context"

log "github.com/micro/go-micro/v2/logger"



hello "hello/proto/hello"

)



type Hello struct{}



func (e *Hello) Handle(ctx context.Context, msg *hello.Message) error {

log.Info("Handler Received message: ", msg.Say)

return nil

}

Either a function or a method of a struct can be used for message processing. As long as its signature is func(context.Context, v interface{}) error

Note that the second parameter of our handler function is *hello.Message , which is defined in the .proto file. Micro automatically decodes the message so we can use it directly in message handlers.

After preparing the message handler, you need to register it. The relevant codes of ./main.go are as follows:

...

// Register Struct as Subscriber

micro.RegisterSubscriber("com.foo.service.hello", service.Server(), new(subscriber.Hello))

...

The above code registers two message processing handlers, and the handlers will receive messages from the topic named “com.foo.service.hello”.

If you want to more control to the subscription behavior, you need to pass additional parameters to micro.RegisterSubscriber . Let’s look at the signature of this method first:

func RegisterSubscriber(topic string, s server.Server, h interface{}, opts ...server.SubscriberOption) error

The first parameter represents the topic. The second parameter is server.Server , which can be obtained from the service.Server() . The third parameter is the message handler.

The last one is an optional parameter that controls the behavior of the subscription. Its type is server.SubscriberOption . Currently, Micro provides 4 built-in options:

server.DisableAutoAck(), disable auto acknowledging of messages after they have been handled。 server.SubscriberContext(ctx context.Context), set context options to allow broker SubscriberOption passed。 server.InternalSubscriber(b bool)，specifies that a subscriber is not advertised to the discovery system server.SubscriberQueue(n string)，shared queue name distributed messages across subscribers。

Note: I personally think that there are too few options exposed by the framework. If you have higher requirements, say you want to control message persistence or retransmission strategy, you have to turn to the micro.Broker interface. Hope this could be enhanced in future releases

Among the above options, server.SubscriberQueue is the one worth explaining separately.

We know that there is the concept of Queue (or Channel in some platform) in the Pub/Sub model. If multiple subscribers of a topic have their own Queue, then messages will be copied and distributed to different Queues, so that each subscriber can receive all the messages.

Micro creates a globally unique Queue for each subscriber instance by default. If you want to share a queue with multiple instances of subscribers, then you need to explicitly specify the queue name via server.SubscriberQueue :

micro.RegisterSubscriber("com.foo.srv.hello", service.Server(), subscriber.Handler, server.SubscriberQueue("foo_bar"))

In this way, subscriber instances will share the same Queue. Therefore, messages will be distributed to a certain node for processing, avoiding the same message being processed repeatedly.

Considering it is a common scenario in distributed system that multiple service instance is running on different nodes, my suggestion is: Unless you know what you are doing, always explicitly specify the queue name — even if there is only one subscription instance currently. The most common practice is to name the queue with the Topic.

At this point, the Sub part of the Pub/Sub model is ready. Let’s start working on the Pub part.