你可以在结构体中存储值,并在应用程序的不同函数中使用这些值。

huangapple go评论87阅读模式
英文:

How can i store in struct and use the value in different functions throughout the application?

问题

我想将一个 MQTT 客户端存储在一个结构体中,并在整个应用程序中使用该客户端。

我的项目结构如下:

-src
-payloads
-payload.go
-repositories
-repository.go
-main.go

payload.go 的内容如下:

package payload

import MQTT "github.com/eclipse/paho.mqtt.golang"

type MQTTClient struct {
Client MQTT.Client
}

在我的 repository.go 中,我有一个名为 Connect() 的函数,内容如下:

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

}

现在,我在一个 go 协程中使用这个 Connect() 函数和一个 grpc 服务器,代码如下:

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)

}

}

现在,我将 Connect() 函数中的客户端存储到结构体中,但我无法使用它。如何在 main.go 中的某个地方存储客户端,然后在整个应用程序中使用它?

英文:

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 &quot;github.com/eclipse/paho.mqtt.golang&quot;

type MQTTClient struct {
	Client MQTT.Client
}

in my repository.go I have a Connect() function like this:

func Connect() MQTT.Client {

	deviceID := flag.String(&quot;device&quot;, &quot;&quot;, &quot;GCP Device-Id&quot;)
	bridge := struct {
		host *string
		port *string
	}{
		flag.String(&quot;mqtt_host&quot;, &quot;&quot;, &quot;MQTT Bridge Host&quot;),
		flag.String(&quot;mqtt_port&quot;, &quot;&quot;, &quot;MQTT Bridge Port&quot;),
	}
	projectID := flag.String(&quot;project&quot;, &quot;&quot;, &quot;GCP Project ID&quot;)
	registryID := flag.String(&quot;registry&quot;, &quot;&quot;, &quot;Cloud IoT Registry ID (short form)&quot;)
	region := flag.String(&quot;region&quot;, &quot;&quot;, &quot;GCP Region&quot;)
	certsCA := flag.String(&quot;ca_certs&quot;, &quot;&quot;, &quot;Download https://pki.google.com/roots.pem&quot;)
	privateKey := flag.String(&quot;private_key&quot;, &quot;&quot;, &quot;Path to private key file&quot;)

	server := fmt.Sprintf(&quot;ssl://%v:%v&quot;, *bridge.host, *bridge.port)
	topic := struct {
		config    string
		telemetry string
	}{
		config:    fmt.Sprintf(&quot;/devices/%v/config&quot;, *deviceID),
		telemetry: fmt.Sprintf(&quot;/devices/%v/events/&quot;, *deviceID),
	}
	qos := flag.Int(&quot;qos&quot;, 0, &quot;The QoS to subscribe to messages at&quot;)
	clientid := fmt.Sprintf(&quot;projects/%v/locations/%v/registries/%v/devices/%v&quot;,
		*projectID,
		*region,
		*registryID,
		*deviceID,
	)
	log.Println(&quot;[main] Loading Google&#39;s roots&quot;)
	certpool := x509.NewCertPool()
	pemCerts, err := ioutil.ReadFile(*certsCA)
	if err == nil {
		certpool.AppendCertsFromPEM(pemCerts)
	}

	log.Println(&quot;[main] Creating TLS Config&quot;)
	config := &amp;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(&quot;unused&quot;)
	///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(&quot;[main] Load Private Key&quot;)
	keyBytes, err := ioutil.ReadFile(*privateKey)
	if err != nil {
		log.Fatal(err)
	}
	//Parsing key from file
	log.Println(&quot;[main] Parse Private Key&quot;)
	key, err := jwt.ParseECPrivateKeyFromPEM(keyBytes)
	if err != nil {
		log.Fatal(err)
	}
	//Signing JWT with private key
	log.Println(&quot;[main] Sign String&quot;)
	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() &amp;&amp; token.Error() != nil {
			log.Fatal(token.Error())
		}
	}

	client := MQTT.NewClient(connOpts)
	if token := client.Connect(); token.Wait() &amp;&amp; token.Error() != nil {
		fmt.Printf(&quot;Not Connected..Retrying...  %s\n&quot;, server)
	} else {
		fmt.Printf(&quot;Connected to %s\n&quot;, 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(&quot;Server started at port 5005&quot;)
	lis, err := net.Listen(&quot;tcp&quot;, &quot;0.0.0.0:5005&quot;)
	if err != nil {
		log.Fatalf(&quot;Failed to listen: %v&quot;, 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 := &amp;payload.MQTTClient{
			Client: client,
		}
		fmt.Println(res)
		//looking for interupt(Ctrl+C)
		value := &lt;-keepAlive
		//If Ctrl+C is pressed then exit the application
		if value == os.Interrupt {
			fmt.Printf(&quot;Exiting the application&quot;)
			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(&quot;Failed to serve: %v&quot;, 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?

答案1

得分: 2

如果你想在整个代码中都可以使用一个结构体,你可以考虑使用单例模式[1]。

在Go语言中,你可以在一个包中定义一个导出的全局变量,这样所有导入该包的代码都可以使用它。

你可以将客户端放在payload包中(或者根据你的需要放在其他地方,这只是一个示例):

package payload

import MQTT "github.com/eclipse/paho.mqtt.golang"

type MQTTClient struct {
    Client MQTT.Client
}

var SingletonClient *MQTTClient

然后在main.go中,不再使用:

res := &payload.MQTTClient{
    Client: client,
}

而是使用:

payload.SingletonClient = &payload.MQTTClient{
    Client: client,
}

现在,你可以在导入payload包的任何地方使用payload.SingletonClient了。

请注意,单例模式并不被认为是一种良好的实践,最好的做法是在需要使用客户端的地方将其传递给相应的代码。不过有时候单例模式也可能会有用。

请注意,在main.go设置payload.SingletonClient之前,任何试图使用它的代码都会失败,因为客户端尚未初始化,所以你可能需要使用某种锁机制来确保在使用之前全局变量已经被设置。

[1] https://en.wikipedia.org/wiki/Singleton_pattern

英文:

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 &quot;github.com/eclipse/paho.mqtt.golang&quot;

type MQTTClient struct {
    Client MQTT.Client
}

var SingletonClient *MQTTClient

Then in main.go instead of

res := &amp;payload.MQTTClient{
    Client: client,
}

do

payload.SingletonClient = &amp;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

huangapple
  • 本文由 发表于 2021年7月23日 16:10:32
  • 转载请务必保留本文链接:https://go.coder-hub.com/68496040.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定