2025-09-19 19:11:08 +05:45
2025-09-17 06:50:38 +05:45
2025-03-29 23:03:27 +05:45
2025-09-18 18:26:35 +05:45
2025-09-19 19:11:08 +05:45
2025-09-18 18:26:35 +05:45
2025-08-16 12:25:55 +05:45
2025-09-18 18:26:35 +05:45
2025-09-19 17:43:24 +05:45
2025-09-18 18:26:35 +05:45
2025-09-18 18:26:35 +05:45
2025-09-17 06:50:38 +05:45
2024-10-21 21:49:43 +05:45
2025-07-31 09:31:28 +05:45
2025-08-10 22:17:58 +05:45
2025-09-18 18:26:35 +05:45
2025-08-02 16:17:20 +05:45
2025-09-18 18:26:35 +05:45
2025-07-31 14:26:08 +05:45
2025-09-18 18:26:35 +05:45
2025-09-18 13:36:24 +05:45
2025-09-18 13:36:24 +05:45
2025-08-10 09:59:00 +05:45
2025-09-18 18:26:35 +05:45
2025-09-18 18:26:35 +05:45
2025-09-18 18:26:35 +05:45
2025-09-18 18:26:35 +05:45
2025-09-18 18:26:35 +05:45
2025-07-31 09:31:28 +05:45
2025-09-17 07:00:43 +05:45
2025-08-16 12:25:55 +05:45
2025-07-31 14:27:27 +05:45
2025-09-18 18:26:35 +05:45
2025-05-05 09:07:01 +05:45
2025-09-18 18:26:35 +05:45
2025-09-18 18:26:35 +05:45
2025-09-18 13:36:24 +05:45

Introduction MQ (Message Queue Broker)

A simple Pub/Sub system memory based task processing. It uses centralized server to manage consumers and publishers.

Examples:

Run server

go run server.go

Run consumer

go run consumer.go

Run publisher

go run publisher.go

tasks.go

package tasks

import (
	"context"
	"github.com/oarkflow/json"
	"fmt"
	"log"

	"github.com/oarkflow/mq"
)

func Node1(ctx context.Context, task *mq.Task) mq.Result {
	return mq.Result{Payload: task.Payload, TaskID: task.ID}
}

func Node2(ctx context.Context, task *mq.Task) mq.Result {
	return mq.Result{Payload: task.Payload, TaskID: task.ID}
}

func Node3(ctx context.Context, task *mq.Task) mq.Result {
	var user map[string]any
	json.Unmarshal(task.Payload, &user)
	age := int(user["age"].(float64))
	status := "FAIL"
	if age > 20 {
		status = "PASS"
	}
	user["status"] = status
	resultPayload, _ := json.Marshal(user)
	return mq.Result{Payload: resultPayload, Status: status}
}

func Node4(ctx context.Context, task *mq.Task) mq.Result {
	var user map[string]any
	json.Unmarshal(task.Payload, &user)
	user["final"] = "D"
	resultPayload, _ := json.Marshal(user)
	return mq.Result{Payload: resultPayload}
}

func Node5(ctx context.Context, task *mq.Task) mq.Result {
	var user map[string]any
	json.Unmarshal(task.Payload, &user)
	user["salary"] = "E"
	resultPayload, _ := json.Marshal(user)
	return mq.Result{Payload: resultPayload}
}

func Node6(ctx context.Context, task *mq.Task) mq.Result {
	var user map[string]any
	json.Unmarshal(task.Payload, &user)
	resultPayload, _ := json.Marshal(map[string]any{"storage": user})
	return mq.Result{Payload: resultPayload}
}

func Callback(ctx context.Context, task mq.Result) mq.Result {
	fmt.Println("Received task", task.TaskID, "Payload", string(task.Payload), task.Error, task.Topic)
	return mq.Result{}
}

func NotifyResponse(ctx context.Context, result mq.Result) {
	log.Printf("DAG Final response: TaskID: %s, Payload: %s, Topic: %s", result.TaskID, result.Payload, result.Topic)
}

Start Server

server.go

package main

import (
	"context"

	"github.com/oarkflow/mq"
	"github.com/oarkflow/mq/examples/tasks"
)

func main() {
	b := mq.NewBroker(mq.WithCallback(tasks.Callback))
	b.NewQueue("queue1")
	b.NewQueue("queue2")
	b.Start(context.Background())
}

Start Consumer

consumer.go

package main

import (
	"context"

	"github.com/oarkflow/mq"

	"github.com/oarkflow/mq/examples/tasks"
)

func main() {
	consumer1 := mq.NewConsumer("consumer-1", "queue1", tasks.Node1)
	consumer2 := mq.NewConsumer("consumer-2", "queue2", tasks.Node2)
	// consumer := mq.NewConsumer("consumer-1", mq.WithTLS(true, "./certs/server.crt", "./certs/server.key"))
	go consumer1.Consume(context.Background())
	consumer2.Consume(context.Background())
}

Publish tasks

publisher.go

package main

import (
	"context"
	"fmt"

	"github.com/oarkflow/mq"
)

func main() {
	payload := []byte(`{"message":"Message Publisher \n Task"}`)
	task := mq.Task{
		Payload: payload,
	}
	publisher := mq.NewPublisher("publish-1")
	err := publisher.Publish(context.Background(), "queue1", task)
	if err != nil {
		panic(err)
	}
	fmt.Println("Async task published successfully")
	payload = []byte(`{"message":"Fire-and-Forget \n Task"}`)
	task = mq.Task{
		Payload: payload,
	}
	result, err := publisher.Request(context.Background(), "queue1", task)
	if err != nil {
		panic(err)
	}
	fmt.Printf("Sync task published. Result: %v\n", string(result.Payload))
}

DAG (Directed Acyclic Graph)

In this package, you can use the DAG feature to create a directed acyclic graph of tasks. The DAG feature allows you to define a sequence of tasks that need to be executed in a specific order.

Example

dag.go

package main

import (
	"context"
	"github.com/oarkflow/json"
	"github.com/oarkflow/mq/consts"
	"github.com/oarkflow/mq/examples/tasks"
	"io"
	"net/http"

	"github.com/oarkflow/mq"
	"github.com/oarkflow/mq/dag"
)

var (
	d = dag.NewDAG(mq.WithSyncMode(false), mq.WithNotifyResponse(tasks.NotifyResponse))
	// d = dag.NewDAG(mq.WithSyncMode(true), mq.WithTLS(true, "./certs/server.crt", "./certs/server.key"), mq.WithCAPath("./certs/ca.cert"))
)

func main() {
	d.AddNode("A", tasks.Node1, true)
	d.AddNode("B", tasks.Node2)
	d.AddNode("C", tasks.Node3)
	d.AddNode("D", tasks.Node4)
	d.AddNode("E", tasks.Node5)
	d.AddNode("F", tasks.Node6)
	d.AddEdge("A", "B", dag.LoopEdge)
	d.AddCondition("C", map[string]string{"PASS": "D", "FAIL": "E"})
	d.AddEdge("B", "C")
	d.AddEdge("D", "F")
	d.AddEdge("E", "F")
	http.HandleFunc("POST /publish", requestHandler("publish"))
	http.HandleFunc("POST /request", requestHandler("request"))
	err := d.Start(context.TODO(), ":8083")
	if err != nil {
		panic(err)
	}
}

func requestHandler(requestType string) func(w http.ResponseWriter, r *http.Request) {
	return func(w http.ResponseWriter, r *http.Request) {
		if r.Method != http.MethodPost {
			http.Error(w, "Invalid request method", http.StatusMethodNotAllowed)
			return
		}
		var payload []byte
		if r.Body != nil {
			defer r.Body.Close()
			var err error
			payload, err = io.ReadAll(r.Body)
			if err != nil {
				http.Error(w, "Failed to read request body", http.StatusBadRequest)
				return
			}
		} else {
			http.Error(w, "Empty request body", http.StatusBadRequest)
			return
		}
		ctx := context.Background()
		if requestType == "request" {
			ctx = mq.SetHeaders(ctx, map[string]string{consts.AwaitResponseKey: "true"})
		}
		// ctx = context.WithValue(ctx, "initial_node", "E")
		rs := d.ProcessTask(ctx, payload)
		w.Header().Set("Content-Type", "application/json")
		json.NewEncoder(w).Encode(rs)
	}
}

Middleware System

The DAG system supports a robust middleware system that allows you to add cross-cutting concerns like logging, validation, timing, and more to your DAG nodes.

Global Middlewares

Global middlewares apply to all nodes in the DAG:

flow.Use(LoggingMiddleware, ValidationMiddleware)

Node-Specific Middlewares

You can apply middlewares to specific nodes:

flow.UseNodeMiddlewares(
    dag.NodeMiddleware{
        Node:        "process_a",
        Middlewares: []mq.Handler{TimingMiddleware},
    },
    dag.NodeMiddleware{
        Node:        "process_b",
        Middlewares: []mq.Handler{TimingMiddleware},
    },
)

Middleware Example

middleware_example_main.go

// LoggingMiddleware logs the start and end of task processing
func LoggingMiddleware(ctx context.Context, task *mq.Task) mq.Result {
    log.Printf("Middleware: Starting processing for node %s, task %s", task.Topic, task.ID)

    // Return successful result to continue to next middleware/processor
    return mq.Result{
        Status:  mq.Completed,
        Ctx:     ctx,
        Payload: task.Payload,
    }
}

// ValidationMiddleware validates the task payload
func ValidationMiddleware(ctx context.Context, task *mq.Task) mq.Result {
    if len(task.Payload) == 0 {
        return mq.Result{
            Status: mq.Failed,
            Error:  fmt.Errorf("empty payload not allowed"),
            Ctx:    ctx,
        }
    }

    return mq.Result{
        Status:  mq.Completed,
        Ctx:     ctx,
        Payload: task.Payload,
    }
}

Middleware Execution Order

Middlewares are executed in the following order:

  1. Global middlewares (in the order they were added)
  2. Node-specific middlewares (in the order they were added)
  3. The actual node processor

Each middleware can:

  • Modify the context
  • Modify the task payload
  • Short-circuit processing by returning an error result
  • Continue processing by returning a successful result

TODOS

  • Backend for task persistence
  • Task scheduling
Description
No description provided
Readme MIT 54 MiB
Languages
Go 100%