Reputation: 1238
I would like to store a mqtt client in a struct and use this client throughout the application.
My project structure looks like this:
-src
-payloads
-payload.go
-repositories
-repository.go
-main.go
payload.go looks like this:
package payload
import MQTT "github.com/eclipse/paho.mqtt.golang"
type MQTTClient struct {
Client MQTT.Client
}
in my repository.go
I have a Connect()
function like this:
func Connect() MQTT.Client {
deviceID := flag.String("device", "", "GCP Device-Id")
bridge := struct {
host *string
port *string
}{
flag.String("mqtt_host", "", "MQTT Bridge Host"),
flag.String("mqtt_port", "", "MQTT Bridge Port"),
}
projectID := flag.String("project", "", "GCP Project ID")
registryID := flag.String("registry", "", "Cloud IoT Registry ID (short form)")
region := flag.String("region", "", "GCP Region")
certsCA := flag.String("ca_certs", "", "Download https://pki.google.com/roots.pem")
privateKey := flag.String("private_key", "", "Path to private key file")
server := fmt.Sprintf("ssl://%v:%v", *bridge.host, *bridge.port)
topic := struct {
config string
telemetry string
}{
config: fmt.Sprintf("/devices/%v/config", *deviceID),
telemetry: fmt.Sprintf("/devices/%v/events/", *deviceID),
}
qos := flag.Int("qos", 0, "The QoS to subscribe to messages at")
clientid := fmt.Sprintf("projects/%v/locations/%v/registries/%v/devices/%v",
*projectID,
*region,
*registryID,
*deviceID,
)
log.Println("[main] Loading Google's roots")
certpool := x509.NewCertPool()
pemCerts, err := ioutil.ReadFile(*certsCA)
if err == nil {
certpool.AppendCertsFromPEM(pemCerts)
}
log.Println("[main] Creating TLS Config")
config := &tls.Config{
RootCAs: certpool,
ClientAuth: tls.NoClientCert,
ClientCAs: nil,
Certificates: []tls.Certificate{},
MinVersion: tls.VersionTLS12,
}
flag.Parse()
connOpts := MQTT.NewClientOptions().
AddBroker(server).
SetClientID(clientid).
SetAutoReconnect(true).
SetConnectRetry(true).
SetDefaultPublishHandler(onMessageReceived).
SetConnectionLostHandler(connLostHandler).
SetReconnectingHandler(reconnHandler).
SetTLSConfig(config)
connOpts.SetUsername("unused")
///JWT Generation Starts from Here
token := jwt.New(jwt.SigningMethodES256)
token.Claims = jwt.StandardClaims{
Audience: *projectID,
IssuedAt: time.Now().Unix(),
ExpiresAt: time.Now().Add(24 * time.Hour).Unix(),
}
//Reading key file
log.Println("[main] Load Private Key")
keyBytes, err := ioutil.ReadFile(*privateKey)
if err != nil {
log.Fatal(err)
}
//Parsing key from file
log.Println("[main] Parse Private Key")
key, err := jwt.ParseECPrivateKeyFromPEM(keyBytes)
if err != nil {
log.Fatal(err)
}
//Signing JWT with private key
log.Println("[main] Sign String")
tokenString, err := token.SignedString(key)
if err != nil {
log.Fatal(err)
}
//JWT Generation Ends here
connOpts.SetPassword(tokenString)
connOpts.OnConnect = func(c MQTT.Client) {
if token := c.Subscribe(topic.config, byte(*qos), nil); token.Wait() && token.Error() != nil {
log.Fatal(token.Error())
}
}
client := MQTT.NewClient(connOpts)
if token := client.Connect(); token.Wait() && token.Error() != nil {
fmt.Printf("Not Connected..Retrying... %s\n", server)
} else {
fmt.Printf("Connected to %s\n", server)
}
return client
}
No i am using this Connect() function in a go routine along with a grpc server like this..
func main() {
fmt.Println("Server started at port 5005")
lis, err := net.Listen("tcp", "0.0.0.0:5005")
if err != nil {
log.Fatalf("Failed to listen: %v", err)
}
//Creating keepAlive channel for mqttt subscribe
keepAlive := make(chan os.Signal)
defer close(keepAlive)
go func() {
//Connecting to mqtt client
client := repositories.Connect()
//passing the client to struct
res := &payload.MQTTClient{
Client: client,
}
fmt.Println(res)
//looking for interupt(Ctrl+C)
value := <-keepAlive
//If Ctrl+C is pressed then exit the application
if value == os.Interrupt {
fmt.Printf("Exiting the application")
os.Exit(3)
}
}()
s := grpc.NewServer()
MqttRepository := repositories.NewMqttRepository()
// It creates a new gRPC server instance
rpc.NewMqttServer(s, MqttRepository)
if err := s.Serve(lis); err != nil {
log.Fatalf("Failed to serve: %v", err)
}
}
Now I am storing the client from the Connect()
function into the struct but I am not able to use it. How can I store client somewhere in main.go
and then use it throughout the aplication?
Upvotes: 1
Views: 223
Reputation: 3273
If you want to have a struct available throughout your code, you might want to use a Singleton Pattern [1].
In Go you basically define an exported global variable in a package which will be available by all code that imports the package.
You can have client live in payload
package (or whichever works for you, this is just an example):
package payload
import MQTT "github.com/eclipse/paho.mqtt.golang"
type MQTTClient struct {
Client MQTT.Client
}
var SingletonClient *MQTTClient
Then in main.go
instead of
res := &payload.MQTTClient{
Client: client,
}
do
payload.SingletonClient = &payload.MQTTClient{
Client: client,
}
Voila, now you can use payload.SingletonClient
everywhere you import payload
package.
Be aware, that Singleton pattern is not considered a good practice, it would be much better if you could structure your code to pass the client around where it's needed. Sometimes it may be useful though.
Be aware, that any code that tries to use payload.SingletonClient
before it's set in main.go
will fail because the client will not be initialized yet, so you might want to use some kind of a lock to make sure that the global variable is set before using it.
[1] https://en.wikipedia.org/wiki/Singleton_pattern
Upvotes: 2