|
@@ -1,4 +1,4 @@
|
|
|
-// Copyright 2022 EMQ Technologies Co., Ltd.
|
|
|
+// Copyright 2023 EMQ Technologies Co., Ltd.
|
|
|
//
|
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
// you may not use this file except in compliance with the License.
|
|
@@ -26,101 +26,115 @@ import (
|
|
|
"go.nanomsg.org/mangos/v3"
|
|
|
"go.nanomsg.org/mangos/v3/protocol/pair"
|
|
|
_ "go.nanomsg.org/mangos/v3/transport/ipc"
|
|
|
+ _ "go.nanomsg.org/mangos/v3/transport/tcp"
|
|
|
"sync"
|
|
|
"sync/atomic"
|
|
|
"time"
|
|
|
)
|
|
|
|
|
|
const (
|
|
|
- NeuronTopic = "$$neuron"
|
|
|
- NeuronUrl = "ipc:///tmp/neuron-ekuiper.ipc"
|
|
|
+ TopicPrefix = "$$neuron_"
|
|
|
+ DefaultNeuronUrl = "ipc:///tmp/neuron-ekuiper.ipc"
|
|
|
)
|
|
|
|
|
|
+type conninfo struct {
|
|
|
+ count int
|
|
|
+ sock mangos.Socket
|
|
|
+ opened int32
|
|
|
+}
|
|
|
+
|
|
|
var (
|
|
|
- m sync.RWMutex
|
|
|
- connectionCount int
|
|
|
- sock mangos.Socket
|
|
|
- opened int32
|
|
|
- sendTimeout = 100
|
|
|
+ m sync.RWMutex
|
|
|
+ connectionReg = make(map[string]*conninfo)
|
|
|
+ sendTimeout = 100
|
|
|
)
|
|
|
|
|
|
// createOrGetNeuronConnection creates a new neuron connection or returns an existing one
|
|
|
// This is the entry function for creating a neuron connection singleton
|
|
|
// The context is from a rule, but the singleton will server for multiple rules
|
|
|
-func createOrGetConnection(sc api.StreamContext, url string) error {
|
|
|
+func createOrGetConnection(sc api.StreamContext, url string) (*conninfo, error) {
|
|
|
m.Lock()
|
|
|
defer m.Unlock()
|
|
|
- sc.GetLogger().Infof("createOrGetConnection count: %d", connectionCount)
|
|
|
- if connectionCount == 0 {
|
|
|
- sc.GetLogger().Infof("Creating neuron connection")
|
|
|
- contextLogger := conf.Log.WithField("neuron_connection", 0)
|
|
|
+ sc.GetLogger().Infof("createOrGetConnection for %s", url)
|
|
|
+ info, ok := connectionReg[url]
|
|
|
+ if !ok || info.count <= 0 {
|
|
|
+ sc.GetLogger().Infof("Creating neuron connection for %s", url)
|
|
|
+ contextLogger := conf.Log.WithField("neuron_connection_url", url)
|
|
|
ctx := kctx.WithValue(kctx.Background(), kctx.LoggerKey, contextLogger)
|
|
|
- ruleId := "$$neuron_connection"
|
|
|
- opId := "$$neuron_connection"
|
|
|
+ ruleId := "$$neuron_connection_" + url
|
|
|
+ opId := "$$neuron_connection_" + url
|
|
|
store, err := state.CreateStore(ruleId, 0)
|
|
|
if err != nil {
|
|
|
ctx.GetLogger().Errorf("neuron connection create store error %v", err)
|
|
|
- return err
|
|
|
+ return nil, err
|
|
|
}
|
|
|
sctx := ctx.WithMeta(ruleId, opId, store)
|
|
|
- err = connect(sctx, url)
|
|
|
+ info = &conninfo{count: 0}
|
|
|
+ connectionReg[url] = info
|
|
|
+ err = connect(sctx, url, info)
|
|
|
if err != nil {
|
|
|
- return err
|
|
|
+ return nil, err
|
|
|
}
|
|
|
- sc.GetLogger().Infof("Neuron connected")
|
|
|
- pubsub.CreatePub(NeuronTopic)
|
|
|
- go run(sctx)
|
|
|
+ sc.GetLogger().Infof("Neuron %s connected", url)
|
|
|
+ pubsub.CreatePub(TopicPrefix + url)
|
|
|
+ go run(sctx, info, url)
|
|
|
}
|
|
|
- connectionCount++
|
|
|
- return nil
|
|
|
+ info.count++
|
|
|
+ return info, nil
|
|
|
}
|
|
|
|
|
|
func closeConnection(ctx api.StreamContext, url string) error {
|
|
|
m.Lock()
|
|
|
defer m.Unlock()
|
|
|
- ctx.GetLogger().Infof("closeConnection count: %d", connectionCount)
|
|
|
- pubsub.RemovePub(NeuronTopic)
|
|
|
- if connectionCount == 1 {
|
|
|
- err := disconnect(url)
|
|
|
- if err != nil {
|
|
|
- return err
|
|
|
+ ctx.GetLogger().Infof("closeConnection %s", url)
|
|
|
+ info, ok := connectionReg[url]
|
|
|
+ if !ok {
|
|
|
+ return fmt.Errorf("no connection for %s", url)
|
|
|
+ }
|
|
|
+ pubsub.RemovePub(TopicPrefix + url)
|
|
|
+ if info.count == 1 {
|
|
|
+ if info.sock != nil {
|
|
|
+ err := info.sock.Close()
|
|
|
+ if err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
- connectionCount--
|
|
|
+ info.count--
|
|
|
return nil
|
|
|
}
|
|
|
|
|
|
// nng connections
|
|
|
|
|
|
// connect to nng
|
|
|
-func connect(ctx api.StreamContext, url string) error {
|
|
|
+func connect(ctx api.StreamContext, url string, info *conninfo) error {
|
|
|
var err error
|
|
|
- sock, err = pair.NewSocket()
|
|
|
+ info.sock, err = pair.NewSocket()
|
|
|
if err != nil {
|
|
|
return err
|
|
|
}
|
|
|
// options consider to export
|
|
|
- err = sock.SetOption(mangos.OptionSendDeadline, time.Duration(sendTimeout)*time.Millisecond)
|
|
|
+ err = info.sock.SetOption(mangos.OptionSendDeadline, time.Duration(sendTimeout)*time.Millisecond)
|
|
|
if err != nil {
|
|
|
return err
|
|
|
}
|
|
|
- sock.SetPipeEventHook(func(ev mangos.PipeEvent, p mangos.Pipe) {
|
|
|
+ info.sock.SetPipeEventHook(func(ev mangos.PipeEvent, p mangos.Pipe) {
|
|
|
switch ev {
|
|
|
case mangos.PipeEventAttached:
|
|
|
- atomic.StoreInt32(&opened, 1)
|
|
|
+ atomic.StoreInt32(&info.opened, 1)
|
|
|
conf.Log.Infof("neuron connection attached")
|
|
|
case mangos.PipeEventAttaching:
|
|
|
conf.Log.Infof("neuron connection is attaching")
|
|
|
case mangos.PipeEventDetached:
|
|
|
- atomic.StoreInt32(&opened, 0)
|
|
|
+ atomic.StoreInt32(&info.opened, 0)
|
|
|
conf.Log.Warnf("neuron connection detached")
|
|
|
- pubsub.ProduceError(ctx, NeuronTopic, fmt.Errorf("neuron connection detached"))
|
|
|
+ pubsub.ProduceError(ctx, TopicPrefix+url, fmt.Errorf("neuron connection detached"))
|
|
|
}
|
|
|
})
|
|
|
//sock.SetOption(mangos.OptionWriteQLen, 100)
|
|
|
//sock.SetOption(mangos.OptionReadQLen, 100)
|
|
|
//sock.SetOption(mangos.OptionBestEffort, false)
|
|
|
- if err = sock.DialOptions(url, map[string]interface{}{
|
|
|
+ if err = info.sock.DialOptions(url, map[string]interface{}{
|
|
|
mangos.OptionDialAsynch: true, // will not report error and keep connecting
|
|
|
mangos.OptionMaxReconnectTime: 5 * time.Second,
|
|
|
mangos.OptionReconnectTime: 100 * time.Millisecond,
|
|
@@ -133,11 +147,11 @@ func connect(ctx api.StreamContext, url string) error {
|
|
|
|
|
|
// run the loop to receive message from the nng connection singleton
|
|
|
// exit when connection is closed
|
|
|
-func run(ctx api.StreamContext) {
|
|
|
+func run(ctx api.StreamContext, info *conninfo, url string) {
|
|
|
ctx.GetLogger().Infof("neuron source receiving loop started")
|
|
|
for {
|
|
|
// no receiving deadline, will wait until the socket closed
|
|
|
- if msg, err := sock.Recv(); err == nil {
|
|
|
+ if msg, err := info.sock.Recv(); err == nil {
|
|
|
ctx.GetLogger().Debugf("neuron received message %s", string(msg))
|
|
|
result := make(map[string]interface{})
|
|
|
err := json.Unmarshal(msg, &result)
|
|
@@ -145,7 +159,7 @@ func run(ctx api.StreamContext) {
|
|
|
ctx.GetLogger().Errorf("neuron decode message error %v", err)
|
|
|
continue
|
|
|
}
|
|
|
- pubsub.Produce(ctx, NeuronTopic, result)
|
|
|
+ pubsub.Produce(ctx, TopicPrefix+url, result)
|
|
|
} else if err == mangos.ErrClosed {
|
|
|
ctx.GetLogger().Infof("neuron connection closed, exit receiving loop")
|
|
|
return
|
|
@@ -155,20 +169,10 @@ func run(ctx api.StreamContext) {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-func publish(ctx api.StreamContext, data []byte) error {
|
|
|
+func publish(ctx api.StreamContext, data []byte, info *conninfo) error {
|
|
|
ctx.GetLogger().Debugf("publish to neuron: %s", string(data))
|
|
|
- if sock != nil && atomic.LoadInt32(&opened) == 1 {
|
|
|
- return sock.Send(data)
|
|
|
+ if info.sock != nil && atomic.LoadInt32(&info.opened) == 1 {
|
|
|
+ return info.sock.Send(data)
|
|
|
}
|
|
|
return fmt.Errorf("%s: neuron connection is not established", errorx.IOErr)
|
|
|
}
|
|
|
-
|
|
|
-func disconnect(_ string) error {
|
|
|
- if sock != nil {
|
|
|
- err := sock.Close()
|
|
|
- if err != nil {
|
|
|
- return err
|
|
|
- }
|
|
|
- }
|
|
|
- return nil
|
|
|
-}
|