mirror of
https://github.com/oarkflow/mq.git
synced 2025-10-05 16:06:55 +08:00
feat: add example
This commit is contained in:
5
ctx.go
5
ctx.go
@@ -8,6 +8,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/oarkflow/xid"
|
"github.com/oarkflow/xid"
|
||||||
@@ -38,7 +39,11 @@ func IsClosed(conn net.Conn) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var m = sync.RWMutex{}
|
||||||
|
|
||||||
func SetHeaders(ctx context.Context, headers map[string]string) context.Context {
|
func SetHeaders(ctx context.Context, headers map[string]string) context.Context {
|
||||||
|
m.Lock()
|
||||||
|
defer m.Unlock()
|
||||||
hd, ok := GetHeaders(ctx)
|
hd, ok := GetHeaders(ctx)
|
||||||
if !ok {
|
if !ok {
|
||||||
hd = make(map[string]string)
|
hd = make(map[string]string)
|
||||||
|
@@ -176,7 +176,7 @@ func (d *DAG) Send(ctx context.Context, payload []byte) mq.Result {
|
|||||||
|
|
||||||
func (d *DAG) processNode(ctx context.Context, task mq.Result) mq.Result {
|
func (d *DAG) processNode(ctx context.Context, task mq.Result) mq.Result {
|
||||||
if con, ok := d.nodes[task.Topic]; ok {
|
if con, ok := d.nodes[task.Topic]; ok {
|
||||||
return con.ProcessTask(ctx, mq.Task{
|
return con.ProcessTask(ctx, &mq.Task{
|
||||||
ID: task.TaskID,
|
ID: task.TaskID,
|
||||||
Payload: task.Payload,
|
Payload: task.Payload,
|
||||||
})
|
})
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
|
/*
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
@@ -46,13 +47,13 @@ func main() {
|
|||||||
}()
|
}()
|
||||||
|
|
||||||
time.Sleep(10 * time.Second)
|
time.Sleep(10 * time.Second)
|
||||||
/*d.Prepare()
|
d.Prepare()
|
||||||
http.HandleFunc("POST /publish", requestHandler("publish"))
|
http.HandleFunc("POST /publish", requestHandler("publish"))
|
||||||
http.HandleFunc("POST /request", requestHandler("request"))
|
http.HandleFunc("POST /request", requestHandler("request"))
|
||||||
err := d.Start(context.TODO(), ":8083")
|
err := d.Start(context.TODO(), ":8083")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}*/
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func requestHandler(requestType string) func(w http.ResponseWriter, r *http.Request) {
|
func requestHandler(requestType string) func(w http.ResponseWriter, r *http.Request) {
|
||||||
@@ -89,3 +90,4 @@ func requestHandler(requestType string) func(w http.ResponseWriter, r *http.Requ
|
|||||||
json.NewEncoder(w).Encode(result)
|
json.NewEncoder(w).Encode(result)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
@@ -4,19 +4,21 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
"github.com/oarkflow/mq"
|
"github.com/oarkflow/mq"
|
||||||
"github.com/oarkflow/mq/v2"
|
"github.com/oarkflow/mq/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
func handler1(ctx context.Context, task *mq.Task) mq.Result {
|
func handler1(ctx context.Context, task *mq.Task) mq.Result {
|
||||||
return mq.Result{Payload: task.Payload, Ctx: ctx}
|
return mq.Result{Payload: task.Payload}
|
||||||
}
|
}
|
||||||
|
|
||||||
func handler2(ctx context.Context, task *mq.Task) mq.Result {
|
func handler2(ctx context.Context, task *mq.Task) mq.Result {
|
||||||
var user map[string]any
|
var user map[string]any
|
||||||
json.Unmarshal(task.Payload, &user)
|
json.Unmarshal(task.Payload, &user)
|
||||||
return mq.Result{Payload: task.Payload, Ctx: ctx}
|
return mq.Result{Payload: task.Payload}
|
||||||
}
|
}
|
||||||
|
|
||||||
func handler3(ctx context.Context, task *mq.Task) mq.Result {
|
func handler3(ctx context.Context, task *mq.Task) mq.Result {
|
||||||
@@ -29,7 +31,7 @@ func handler3(ctx context.Context, task *mq.Task) mq.Result {
|
|||||||
}
|
}
|
||||||
user["status"] = status
|
user["status"] = status
|
||||||
resultPayload, _ := json.Marshal(user)
|
resultPayload, _ := json.Marshal(user)
|
||||||
return mq.Result{Payload: resultPayload, Status: status, Ctx: ctx}
|
return mq.Result{Payload: resultPayload, Status: status}
|
||||||
}
|
}
|
||||||
|
|
||||||
func handler4(ctx context.Context, task *mq.Task) mq.Result {
|
func handler4(ctx context.Context, task *mq.Task) mq.Result {
|
||||||
@@ -37,7 +39,7 @@ func handler4(ctx context.Context, task *mq.Task) mq.Result {
|
|||||||
json.Unmarshal(task.Payload, &user)
|
json.Unmarshal(task.Payload, &user)
|
||||||
user["final"] = "D"
|
user["final"] = "D"
|
||||||
resultPayload, _ := json.Marshal(user)
|
resultPayload, _ := json.Marshal(user)
|
||||||
return mq.Result{Payload: resultPayload, Ctx: ctx}
|
return mq.Result{Payload: resultPayload}
|
||||||
}
|
}
|
||||||
|
|
||||||
func handler5(ctx context.Context, task *mq.Task) mq.Result {
|
func handler5(ctx context.Context, task *mq.Task) mq.Result {
|
||||||
@@ -45,34 +47,73 @@ func handler5(ctx context.Context, task *mq.Task) mq.Result {
|
|||||||
json.Unmarshal(task.Payload, &user)
|
json.Unmarshal(task.Payload, &user)
|
||||||
user["salary"] = "E"
|
user["salary"] = "E"
|
||||||
resultPayload, _ := json.Marshal(user)
|
resultPayload, _ := json.Marshal(user)
|
||||||
return mq.Result{Payload: resultPayload, Ctx: ctx}
|
return mq.Result{Payload: resultPayload}
|
||||||
}
|
}
|
||||||
|
|
||||||
func handler6(ctx context.Context, task *mq.Task) mq.Result {
|
func handler6(ctx context.Context, task *mq.Task) mq.Result {
|
||||||
var user map[string]any
|
var user map[string]any
|
||||||
json.Unmarshal(task.Payload, &user)
|
json.Unmarshal(task.Payload, &user)
|
||||||
resultPayload, _ := json.Marshal(map[string]any{"storage": user})
|
resultPayload, _ := json.Marshal(map[string]any{"storage": user})
|
||||||
return mq.Result{Payload: resultPayload, Ctx: ctx}
|
return mq.Result{Payload: resultPayload}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
d = v2.NewDAG(mq.WithSyncMode(true))
|
||||||
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
dag := v2.NewDAG()
|
d.AddNode("A", handler1)
|
||||||
dag.AddNode("A", handler1)
|
d.AddNode("B", handler2)
|
||||||
dag.AddNode("B", handler2)
|
d.AddNode("C", handler3)
|
||||||
dag.AddNode("C", handler3)
|
d.AddNode("D", handler4)
|
||||||
dag.AddNode("D", handler4)
|
d.AddNode("E", handler5)
|
||||||
dag.AddNode("E", handler5)
|
d.AddNode("F", handler6)
|
||||||
dag.AddNode("F", handler6)
|
d.AddEdge("A", "B", v2.LoopEdge)
|
||||||
dag.AddEdge("A", "B", v2.LoopEdge)
|
d.AddCondition("C", map[string]string{"PASS": "D", "FAIL": "E"})
|
||||||
dag.AddCondition("C", map[string]string{"PASS": "D", "FAIL": "E"})
|
d.AddEdge("B", "C")
|
||||||
dag.AddEdge("B", "C")
|
d.AddEdge("D", "F")
|
||||||
dag.AddEdge("D", "F")
|
d.AddEdge("E", "F")
|
||||||
dag.AddEdge("E", "F")
|
|
||||||
|
|
||||||
initialPayload, _ := json.Marshal([]map[string]any{
|
initialPayload, _ := json.Marshal([]map[string]any{
|
||||||
{"user_id": 1, "age": 12},
|
{"user_id": 1, "age": 12},
|
||||||
{"user_id": 2, "age": 34},
|
{"user_id": 2, "age": 34},
|
||||||
})
|
})
|
||||||
rs := dag.ProcessTask(context.Background(), "A", initialPayload)
|
rs := d.ProcessTask(context.Background(), "A", initialPayload)
|
||||||
fmt.Println(string(rs.Payload))
|
fmt.Println(string(rs.Payload))
|
||||||
|
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
|
||||||
|
}
|
||||||
|
rs := d.ProcessTask(context.Background(), "A", payload)
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
result := map[string]any{
|
||||||
|
"message_id": rs.TaskID,
|
||||||
|
"payload": string(rs.Payload),
|
||||||
|
"error": rs.Error,
|
||||||
|
}
|
||||||
|
json.NewEncoder(w).Encode(result)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -8,7 +8,6 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type Result struct {
|
type Result struct {
|
||||||
Ctx context.Context
|
|
||||||
Payload json.RawMessage `json:"payload"`
|
Payload json.RawMessage `json:"payload"`
|
||||||
Topic string `json:"topic"`
|
Topic string `json:"topic"`
|
||||||
TaskID string `json:"task_id"`
|
TaskID string `json:"task_id"`
|
||||||
@@ -38,7 +37,6 @@ func HandleError(ctx context.Context, err error, status ...string) Result {
|
|||||||
return Result{
|
return Result{
|
||||||
Status: st,
|
Status: st,
|
||||||
Error: err,
|
Error: err,
|
||||||
Ctx: ctx,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -50,7 +48,6 @@ func (r Result) WithData(status string, data []byte) Result {
|
|||||||
Status: status,
|
Status: status,
|
||||||
Payload: data,
|
Payload: data,
|
||||||
Error: nil,
|
Error: nil,
|
||||||
Ctx: r.Ctx,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
40
v2/dag.go
40
v2/dag.go
@@ -3,6 +3,8 @@ package v2
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/oarkflow/xid"
|
"github.com/oarkflow/xid"
|
||||||
@@ -44,17 +46,51 @@ type Edge struct {
|
|||||||
|
|
||||||
type DAG struct {
|
type DAG struct {
|
||||||
Nodes map[string]*Node
|
Nodes map[string]*Node
|
||||||
|
server *mq.Broker
|
||||||
taskContext map[string]*TaskManager
|
taskContext map[string]*TaskManager
|
||||||
conditions map[string]map[string]string
|
conditions map[string]map[string]string
|
||||||
mu sync.RWMutex
|
mu sync.RWMutex
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewDAG() *DAG {
|
func NewDAG(opts ...mq.Option) *DAG {
|
||||||
return &DAG{
|
d := &DAG{
|
||||||
Nodes: make(map[string]*Node),
|
Nodes: make(map[string]*Node),
|
||||||
taskContext: make(map[string]*TaskManager),
|
taskContext: make(map[string]*TaskManager),
|
||||||
conditions: make(map[string]map[string]string),
|
conditions: make(map[string]map[string]string),
|
||||||
}
|
}
|
||||||
|
opts = append(opts, mq.WithCallback(d.onTaskCallback))
|
||||||
|
d.server = mq.NewBroker(opts...)
|
||||||
|
return d
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tm *DAG) onTaskCallback(ctx context.Context, result mq.Result) mq.Result {
|
||||||
|
if taskContext, ok := tm.taskContext[result.TaskID]; ok {
|
||||||
|
return taskContext.handleCallback(ctx, result)
|
||||||
|
}
|
||||||
|
return mq.Result{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tm *DAG) Start(ctx context.Context, addr string) error {
|
||||||
|
if tm.server.SyncMode() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
go func() {
|
||||||
|
err := tm.server.Start(ctx)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
for _, con := range tm.Nodes {
|
||||||
|
go func(con *Node) {
|
||||||
|
con.consumer.Consume(ctx)
|
||||||
|
}(con)
|
||||||
|
}
|
||||||
|
log.Printf("HTTP server started on %s", addr)
|
||||||
|
config := tm.server.TLSConfig()
|
||||||
|
if config.UseTLS {
|
||||||
|
return http.ListenAndServeTLS(addr, config.CertPath, config.KeyPath, nil)
|
||||||
|
}
|
||||||
|
return http.ListenAndServe(addr, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tm *DAG) AddNode(key string, handler mq.Handler) {
|
func (tm *DAG) AddNode(key string, handler mq.Handler) {
|
||||||
|
@@ -46,13 +46,18 @@ func (tm *TaskManager) processTask(ctx context.Context, nodeID string, task *mq.
|
|||||||
tm.mutex.Lock()
|
tm.mutex.Lock()
|
||||||
defer tm.mutex.Unlock()
|
defer tm.mutex.Unlock()
|
||||||
if len(tm.results) == 1 {
|
if len(tm.results) == 1 {
|
||||||
return tm.callback(ctx, tm.results[0])
|
return tm.handleResult(ctx, tm.results[0])
|
||||||
}
|
}
|
||||||
return tm.callback(ctx, tm.results)
|
return tm.handleResult(ctx, tm.results)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tm *TaskManager) callback(ctx context.Context, results any) mq.Result {
|
func (tm *TaskManager) handleCallback(ctx context.Context, result mq.Result) mq.Result {
|
||||||
|
fmt.Println(string(result.Payload), result.Topic, result.TaskID)
|
||||||
|
return mq.Result{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tm *TaskManager) handleResult(ctx context.Context, results any) mq.Result {
|
||||||
var rs mq.Result
|
var rs mq.Result
|
||||||
switch res := results.(type) {
|
switch res := results.(type) {
|
||||||
case []mq.Result:
|
case []mq.Result:
|
||||||
@@ -101,17 +106,21 @@ func (tm *TaskManager) processNode(ctx context.Context, node *Node, task *mq.Tas
|
|||||||
return
|
return
|
||||||
default:
|
default:
|
||||||
ctx = mq.SetHeaders(ctx, map[string]string{consts.QueueKey: node.Key})
|
ctx = mq.SetHeaders(ctx, map[string]string{consts.QueueKey: node.Key})
|
||||||
result = node.consumer.ProcessTask(ctx, task)
|
if tm.dag.server.SyncMode() {
|
||||||
result.Topic = node.Key
|
result = node.consumer.ProcessTask(ctx, task)
|
||||||
if result.Error != nil {
|
result.Topic = node.Key
|
||||||
tm.appendFinalResult(result)
|
if result.Error != nil {
|
||||||
return
|
tm.appendFinalResult(result)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
err := tm.dag.server.Publish(ctx, *task, node.Key)
|
||||||
|
if err != nil {
|
||||||
|
tm.appendFinalResult(mq.Result{Error: err})
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if result.Ctx == nil {
|
|
||||||
result.Ctx = ctx
|
|
||||||
}
|
|
||||||
ctx = result.Ctx
|
|
||||||
tm.mutex.Lock()
|
tm.mutex.Lock()
|
||||||
task.Results[node.Key] = result
|
task.Results[node.Key] = result
|
||||||
tm.mutex.Unlock()
|
tm.mutex.Unlock()
|
||||||
|
Reference in New Issue
Block a user