1
0
mirror of https://github.com/mainflux/mainflux.git synced 2025-04-29 13:49:28 +08:00
Dušan Borovčanin c3019fffb6
NOISSUE - Refactor messaging (#1141)
* Refactor messaging

Signed-off-by: Dušan Borovčanin <dusan.borovcanin@mainflux.com>

* Rename SubscribeHandler to MessageHandler

Signed-off-by: Dušan Borovčanin <dusan.borovcanin@mainflux.com>

* Remove `Auth` event logs

Signed-off-by: Dušan Borovčanin <dusan.borovcanin@mainflux.com>

* Update message pubsub APi

Signed-off-by: Dušan Borovčanin <dusan.borovcanin@mainflux.com>

* Fix topics handling

Signed-off-by: Dušan Borovčanin <dusan.borovcanin@mainflux.com>

* Update CoAP adapter

Signed-off-by: Dušan Borovčanin <dusan.borovcanin@mainflux.com>

* Update Twins service

Signed-off-by: Dušan Borovčanin <dusan.borovcanin@mainflux.com>

* Update LoRa adapter

Signed-off-by: Dušan Borovčanin <dusan.borovcanin@mainflux.com>

* Update OPC UA adapter

Signed-off-by: Dušan Borovčanin <dusan.borovcanin@mainflux.com>

* Remove broker package

Package `broker` is conceptually renamed to package `nats`.

Signed-off-by: Dušan Borovčanin <dusan.borovcanin@mainflux.com>

* Update makefile

Signed-off-by: Dušan Borovčanin <dusan.borovcanin@mainflux.com>

* Add comment explanation

Signed-off-by: Dušan Borovčanin <dusan.borovcanin@mainflux.com>

* Fix MQTT adapter

Signed-off-by: Dušan Borovčanin <dusan.borovcanin@mainflux.com>

* Fix typo

Signed-off-by: Dušan Borovčanin <dusan.borovcanin@mainflux.com>

* Move NATS pub/sub implementation to pubsub pkg

Signed-off-by: Dušan Borovčanin <dusan.borovcanin@mainflux.com>

* Remove an empty line in main methods

Signed-off-by: Dušan Borovčanin <dusan.borovcanin@mainflux.com>

* Move messaging-related code to messaging package

Signed-off-by: Dušan Borovčanin <dusan.borovcanin@mainflux.com>

* Fix Twins mocks

Signed-off-by: Dušan Borovčanin <dusan.borovcanin@mainflux.com>

* Change Occurred back to Created

Signed-off-by: Dušan Borovčanin <dusan.borovcanin@mainflux.com>

* Fix tranformer test

Signed-off-by: Dušan Borovčanin <dusan.borovcanin@mainflux.com>

* Fix message proto commands

Signed-off-by: Dušan Borovčanin <dusan.borovcanin@mainflux.com>

* Replace string literal with constant

Signed-off-by: Dušan Borovčanin <dusan.borovcanin@mainflux.com>

* Remove alias from main method

Signed-off-by: Dušan Borovčanin <dusan.borovcanin@mainflux.com>

* Change messaging pubsub alias

Signed-off-by: Dušan Borovčanin <dusan.borovcanin@mainflux.com>

* Rename occured to created

Signed-off-by: Dušan Borovčanin <dusan.borovcanin@mainflux.com>

* Handle NATS connection in the NATS PubSub

Signed-off-by: Dušan Borovčanin <dusan.borovcanin@mainflux.com>

* Rename n to pub/pubSub

Signed-off-by: Dušan Borovčanin <dusan.borovcanin@mainflux.com>

* Fix typos

Signed-off-by: Dušan Borovčanin <dusan.borovcanin@mainflux.com>
2020-04-28 11:02:35 +02:00

277 lines
8.1 KiB
Go

package main
import (
"fmt"
"io"
"io/ioutil"
"log"
"net/http"
"os"
"os/signal"
"strconv"
"syscall"
"time"
"github.com/go-redis/redis"
"github.com/mainflux/mainflux"
"github.com/mainflux/mainflux/logger"
"github.com/mainflux/mainflux/messaging"
"github.com/mainflux/mainflux/messaging/nats"
mqtt "github.com/mainflux/mainflux/mqtt"
mr "github.com/mainflux/mainflux/mqtt/redis"
thingsapi "github.com/mainflux/mainflux/things/api/auth/grpc"
mp "github.com/mainflux/mproxy/pkg/mqtt"
"github.com/mainflux/mproxy/pkg/session"
ws "github.com/mainflux/mproxy/pkg/websocket"
opentracing "github.com/opentracing/opentracing-go"
jconfig "github.com/uber/jaeger-client-go/config"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials"
)
const (
// Logging
defLogLevel = "error"
envLogLevel = "MF_MQTT_ADAPTER_LOG_LEVEL"
// MQTT
defMQTTHost = "0.0.0.0"
defMQTTPort = "1883"
defMQTTTargetHost = "0.0.0.0"
defMQTTTargetPort = "1883"
envMQTTHost = "MF_MQTT_ADAPTER_MQTT_HOST"
envMQTTPort = "MF_MQTT_ADAPTER_MQTT_PORT"
envMQTTTargetHost = "MF_MQTT_ADAPTER_MQTT_TARGET_HOST"
envMQTTTargetPort = "MF_MQTT_ADAPTER_MQTT_TARGET_PORT"
// HTTP
defHTTPHost = "0.0.0.0"
defHTTPPort = "8080"
defHTTPScheme = "ws"
defHTTPTargetHost = "localhost"
defHTTPTargetPort = "8080"
defHTTPTargetPath = "/mqtt"
envHTTPHost = "MF_MQTT_ADAPTER_WS_HOST"
envHTTPPort = "MF_MQTT_ADAPTER_WS_PORT"
envHTTPScheme = "MF_MQTT_ADAPTER_WS_SCHEMA"
envHTTPTargetHost = "MF_MQTT_ADAPTER_WS_TARGET_HOST"
envHTTPTargetPort = "MF_MQTT_ADAPTER_WS_TARGET_PORT"
envHTTPTargetPath = "MF_MQTT_ADAPTER_WS_TARGET_PATH"
// Things
defThingsAuthURL = "localhost:8181"
defThingsAuthTimeout = "1" // in seconds
envThingsAuthURL = "MF_THINGS_AUTH_GRPC_URL"
envThingsAuthTimeout = "MF_THINGS_AUTH_GRPC_TIMMEOUT"
// Nats
defNatsURL = "nats://localhost:4222"
envNatsURL = "MF_NATS_URL"
// Jaeger
defJaegerURL = ""
envJaegerURL = "MF_JAEGER_URL"
// TLS
defClientTLS = "false"
defCACerts = ""
envClientTLS = "MF_MQTT_ADAPTER_CLIENT_TLS"
envCACerts = "MF_MQTT_ADAPTER_CA_CERTS"
// Instance
envInstance = "MF_MQTT_ADAPTER_INSTANCE"
defInstance = ""
// ES
envESURL = "MF_MQTT_ADAPTER_ES_URL"
envESPass = "MF_MQTT_ADAPTER_ES_PASS"
envESDB = "MF_MQTT_ADAPTER_ES_DB"
defESURL = "localhost:6379"
defESPass = ""
defESDB = "0"
)
type config struct {
mqttHost string
mqttPort string
mqttTargetHost string
mqttTargetPort string
httpHost string
httpPort string
httpScheme string
httpTargetHost string
httpTargetPort string
httpTargetPath string
jaegerURL string
logLevel string
thingsURL string
thingsAuthURL string
thingsAuthTimeout time.Duration
natsURL string
clientTLS bool
caCerts string
instance string
esURL string
esPass string
esDB string
}
func main() {
cfg := loadConfig()
logger, err := logger.New(os.Stdout, cfg.logLevel)
if err != nil {
log.Fatalf(err.Error())
}
conn := connectToThings(cfg, logger)
defer conn.Close()
tracer, closer := initJaeger("mproxy", cfg.jaegerURL, logger)
defer closer.Close()
thingsTracer, thingsCloser := initJaeger("things", cfg.jaegerURL, logger)
defer thingsCloser.Close()
rc := connectToRedis(cfg.esURL, cfg.esPass, cfg.esDB, logger)
defer rc.Close()
cc := thingsapi.NewClient(conn, thingsTracer, cfg.thingsAuthTimeout)
pub, err := nats.NewPublisher(cfg.natsURL)
if err != nil {
logger.Error(fmt.Sprintf("Failed to connect to NATS: %s", err))
os.Exit(1)
}
defer pub.Close()
es := mr.NewEventStore(rc, cfg.instance)
// Event handler for MQTT hooks
evt := mqtt.New([]messaging.Publisher{pub}, cc, es, logger, tracer)
errs := make(chan error, 2)
logger.Info(fmt.Sprintf("Starting MQTT proxy on port %s", cfg.mqttPort))
go proxyMQTT(cfg, logger, evt, errs)
logger.Info(fmt.Sprintf("Starting MQTT over WS proxy on port %s", cfg.httpPort))
go proxyWS(cfg, logger, evt, errs)
go func() {
c := make(chan os.Signal, 1)
signal.Notify(c, syscall.SIGINT)
errs <- fmt.Errorf("%s", <-c)
}()
err = <-errs
logger.Error(fmt.Sprintf("mProxy terminated: %s", err))
}
func loadConfig() config {
tls, err := strconv.ParseBool(mainflux.Env(envClientTLS, defClientTLS))
if err != nil {
log.Fatalf("Invalid value passed for %s\n", envClientTLS)
}
timeout, err := strconv.ParseInt(mainflux.Env(envThingsAuthTimeout, defThingsAuthTimeout), 10, 64)
if err != nil {
log.Fatalf("Invalid %s value: %s", envThingsAuthTimeout, err.Error())
}
return config{
mqttHost: mainflux.Env(envMQTTHost, defMQTTHost),
mqttPort: mainflux.Env(envMQTTPort, defMQTTPort),
mqttTargetHost: mainflux.Env(envMQTTTargetHost, defMQTTTargetHost),
mqttTargetPort: mainflux.Env(envMQTTTargetPort, defMQTTTargetPort),
httpHost: mainflux.Env(envHTTPHost, defHTTPHost),
httpPort: mainflux.Env(envHTTPPort, defHTTPPort),
httpScheme: mainflux.Env(envHTTPScheme, defHTTPScheme),
httpTargetHost: mainflux.Env(envHTTPTargetHost, defHTTPTargetHost),
httpTargetPort: mainflux.Env(envHTTPTargetPort, defHTTPTargetPort),
httpTargetPath: mainflux.Env(envHTTPTargetPath, defHTTPTargetPath),
jaegerURL: mainflux.Env(envJaegerURL, defJaegerURL),
thingsAuthURL: mainflux.Env(envThingsAuthURL, defThingsAuthURL),
thingsAuthTimeout: time.Duration(timeout) * time.Second,
thingsURL: mainflux.Env(envThingsAuthURL, defThingsAuthURL),
natsURL: mainflux.Env(envNatsURL, defNatsURL),
logLevel: mainflux.Env(envLogLevel, defLogLevel),
clientTLS: tls,
caCerts: mainflux.Env(envCACerts, defCACerts),
instance: mainflux.Env(envInstance, defInstance),
esURL: mainflux.Env(envESURL, defESURL),
esPass: mainflux.Env(envESPass, defESPass),
esDB: mainflux.Env(envESDB, defESDB),
}
}
func initJaeger(svcName, url string, logger logger.Logger) (opentracing.Tracer, io.Closer) {
if url == "" {
return opentracing.NoopTracer{}, ioutil.NopCloser(nil)
}
tracer, closer, err := jconfig.Configuration{
ServiceName: svcName,
Sampler: &jconfig.SamplerConfig{
Type: "const",
Param: 1,
},
Reporter: &jconfig.ReporterConfig{
LocalAgentHostPort: url,
LogSpans: true,
},
}.NewTracer()
if err != nil {
logger.Error(fmt.Sprintf("Failed to init Jaeger client: %s", err))
os.Exit(1)
}
return tracer, closer
}
func connectToThings(cfg config, logger logger.Logger) *grpc.ClientConn {
var opts []grpc.DialOption
if cfg.clientTLS {
if cfg.caCerts != "" {
tpc, err := credentials.NewClientTLSFromFile(cfg.caCerts, "")
if err != nil {
logger.Error(fmt.Sprintf("Failed to load certs: %s", err))
os.Exit(1)
}
opts = append(opts, grpc.WithTransportCredentials(tpc))
}
} else {
logger.Info("gRPC communication is not encrypted")
opts = append(opts, grpc.WithInsecure())
}
conn, err := grpc.Dial(cfg.thingsAuthURL, opts...)
if err != nil {
logger.Error(fmt.Sprintf("Failed to connect to things service: %s", err))
os.Exit(1)
}
return conn
}
func connectToRedis(redisURL, redisPass, redisDB string, logger logger.Logger) *redis.Client {
db, err := strconv.Atoi(redisDB)
if err != nil {
logger.Error(fmt.Sprintf("Failed to connect to redis: %s", err))
os.Exit(1)
}
return redis.NewClient(&redis.Options{
Addr: redisURL,
Password: redisPass,
DB: db,
})
}
func proxyMQTT(cfg config, logger logger.Logger, evt session.Handler, errs chan error) {
address := fmt.Sprintf("%s:%s", cfg.mqttHost, cfg.mqttPort)
target := fmt.Sprintf("%s:%s", cfg.mqttTargetHost, cfg.mqttTargetPort)
mp := mp.New(address, target, evt, logger)
errs <- mp.Proxy()
}
func proxyWS(cfg config, logger logger.Logger, evt session.Handler, errs chan error) {
target := fmt.Sprintf("%s:%s", cfg.httpTargetHost, cfg.httpTargetPort)
wp := ws.New(target, cfg.httpTargetPath, cfg.httpScheme, evt, logger)
http.Handle("/mqtt", wp.Handler())
p := fmt.Sprintf(":%s", cfg.httpPort)
errs <- http.ListenAndServe(p, nil)
}