mqrr

package module
v0.1.5 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Dec 10, 2023 License: MIT Imports: 11 Imported by: 0

README

MQRR

MQTT v5 Request-Response (MQRR) Pattern provides similar behavior like HTTP. Different from the HTTP Request-Response model, MQTT request-response is asynchronous, which brings a problem, that is how to associate the response message with the request message. The following diagram describes the request-response interaction process:

img

See the MQTT Request Response article for detailed description.

Installation

go get github.com/serialt/mqrr

Quick start

Server

Let's create a simple API server that listens for the hello topic.

package main

import (
	"github.com/serialt/mqrr"
)

func main() {
	r := mqrr.New()
	r.Route("hello", func(c *mqrr.Context) {
		c.String("Hello %s", c.GetRawString())
	})
	r.Run("mqtt://broker-cn.emqx.io:1883")
}
Client

In the client side, we publish a message with our name to the hello topic, then wait for a response.

go get github.com/serialt/mqrr/client
package main

import (
	"context"
	"fmt"
	"github.com/eclipse/paho.golang/paho"
	"github.com/serialt/mqrr/client"
)

func main() {
	resp, err := client.Request(context.Background(), "mqtt://broker-cn.emqx.io:1883", &paho.Publish{
		Topic:   "hello",
		Payload: []byte("John"),
	})
	if err != nil {
		panic(err)
	}
	fmt.Println(string(resp.Payload)) // Should output "Hello John"
}

API Examples

Parameters in topic
func main() {
	r := mqrr.New()
	r.Route("user/:name", func(c *mqrr.Context) {
        name := c.Param("name")
		c.String("Hello %s", name)
	})
	r.Route("groups/*last", func(c *mqrr.Context) {
		c.String(c.Param("last"))
	})
	r.Run("mqtt://broker-cn.emqx.io:1883")
}
Grouping routes
func main() {
	r := mqrr.New()
	g1 := r.Group("G1")
	g1.Route(":dev/new", func(c *mqrr.Context) {
		c.String("new")
	})
	g2 := g1.Group("G2")
	g2.Route(":temp", func(c *mqrr.Context) {
		c.String("temp")
	})
	r.Run("mqtt://broker-cn.emqx.io:1883")
}
Data binding
type User struct {
	Name string `topic:"name"`
	Age  int    `json:"age" validate:"gte=18"`
}

func main() {
	r := mqrr.New()
	r.Route("user/:name", func(c *mqrr.Context) {
		user := User{}
		if err := c.BindTopic(&user); err != nil {
			c.String(err.Error())
			return
		}
		if err := c.ShouldBindJSON(&user); err != nil {
			c.String(err.Error())
			return
		}
		c.JSON(user)
	})
	r.Run("mqtt://broker-cn.emqx.io:1883")
}
Client requests in same connection
func main() {
	c, err := client.New("mqtt://broker-cn.emqx.io:1883")
	if err != nil {
		panic(err)
	}
	defer c.Close(context.Background())
	wg := sync.WaitGroup{}
	for _, name := range []string{"John", "Mary", "Ben"} {
		wg.Add(1)
		go func(n string) {
			defer wg.Done()
			resp, err := c.Request(context.Background(), &paho.Publish{
				Topic:   "hello",
				Payload: []byte(n),
			})
			if err != nil {
				panic(err)
			}
			fmt.Println(string(resp.Payload))
		}(name)
	}
	wg.Wait()
}

Documentation

Index

Constants

View Source
const (
	DebugMode   = "debug"
	ReleaseMode = "release"
)

Variables

This section is empty.

Functions

func IsDebugging

func IsDebugging() bool

IsDebugging returns true if the framework is running in debug mode. Use SetMode(mqrr.ReleaseMode) to disable debug mode.

func SetMode

func SetMode(value string)

SetMode sets running mode according to input string.

Types

type Context

type Context struct {
	Request *paho.Publish
	Params  map[string][]string
	// contains filtered or unexported fields
}

Context is a data container. It allows us to pass variables across different procedures, bind request data, validate struct and render response.

func (*Context) BindJSON

func (c *Context) BindJSON(obj interface{}) error

BindJSON deserializes the request payload and binds the passed struct pointer. e.g. `json:"var1"`.

func (*Context) BindText

func (c *Context) BindText(obj interface{}) error

BindText slices the request string into all substrings separated by given separator, then binds the passed struct pointer according to the given slice index. e.g. `text:"2,-"`, `text:"0"`.

func (*Context) BindTopic

func (c *Context) BindTopic(obj interface{}) error

BindTopic binds the passed struct pointer using the topic parameters. e.g. `topic:"var1"`.

func (*Context) Data

func (c *Context) Data(data []byte)

Data writes raw data into the response data.

func (*Context) GetRawData

func (c *Context) GetRawData() []byte

GetRawData return raw payload data.

func (*Context) GetRawString

func (c *Context) GetRawString() string

GetRawString return raw payload data as string.

func (*Context) JSON

func (c *Context) JSON(t interface{})

JSON serializes the given struct as JSON into the response data.

func (*Context) Param

func (c *Context) Param(key string) string

Param returns the value of the topic param.

func (*Context) ShouldBindJSON

func (c *Context) ShouldBindJSON(obj interface{}) error

ShouldBindJSON is a combiner of BindJSON and binder.Validate.

func (*Context) ShouldBindText

func (c *Context) ShouldBindText(obj interface{}) error

ShouldBindText is a combiner of BindText and binder.Validate.

func (*Context) ShouldBindTopic

func (c *Context) ShouldBindTopic(obj interface{}) error

ShouldBindTopic is a combiner of BindTopic and binder.Validate.

func (*Context) String

func (c *Context) String(format string, values ...interface{})

String writes the given string into the response data.

type Engine

type Engine struct {
	RouterGroup
	BaseTopic string
	// contains filtered or unexported fields
}

Engine is the server instance, it contains the connection manager, router and subscriptions. Create an instance of Engine, by using New().

func New

func New() *Engine

New returns a new server instance.

func (*Engine) Close

func (engine *Engine) Close(ctx context.Context) error

Close closes the connection and waits for goroutine to exit.

func (*Engine) Route

func (engine *Engine) Route(topic string, handler func(c *Context))

Route registers a request handler with the given topic. A topic contains multiple levels, each level is separated by a forward slash. A level can be a name, or wildcards like `+` and `#`, or a named variable starts with `:` and `*`.

func (*Engine) Run

func (engine *Engine) Run(broker string)

Run connects to the given MQTT broker, then starts listening requests.

func (*Engine) RunCfg

func (engine *Engine) RunCfg(cc autopaho.ClientConfig)

RunCfg connects to the MQTT broker using the given client config, then starts listening requests.

func (*Engine) RunUser

func (engine *Engine) RunUser(broker, user, password string)

RunUser connects to the MQTT broker using auth user and password, then starts listening requests.

type RouterGroup

type RouterGroup struct {
	// contains filtered or unexported fields
}

RouterGroup is associated with a topic prefix. In the Route call, it joins all the topic levels to form a full topic.

func (*RouterGroup) Group

func (g *RouterGroup) Group(base string) *RouterGroup

Group creates a new router group with the given topic prefix.

func (*RouterGroup) Route

func (g *RouterGroup) Route(topic string, handler func(c *Context))

Route registers a request handler with the given topic. See Engine.Route for detail.

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL