github.com/jingruilea/kubeedge@v1.2.0-beta.0.0.20200410162146-4bb8902b3879/edge/pkg/eventbus/mqtt/server.go (about)

     1  /*
     2  Copyright 2019 The KubeEdge Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8     http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package mqtt
    18  
    19  import (
    20  	"encoding/base64"
    21  	"fmt"
    22  	"strings"
    23  
    24  	"github.com/256dpi/gomqtt/broker"
    25  	"github.com/256dpi/gomqtt/packet"
    26  	"github.com/256dpi/gomqtt/topic"
    27  	"github.com/256dpi/gomqtt/transport"
    28  	"k8s.io/klog"
    29  
    30  	beehiveContext "github.com/kubeedge/beehive/pkg/core/context"
    31  	"github.com/kubeedge/beehive/pkg/core/model"
    32  	"github.com/kubeedge/kubeedge/edge/pkg/common/modules"
    33  )
    34  
    35  //Server serve as an internal mqtt broker.
    36  type Server struct {
    37  	// Internal mqtt url
    38  	url string
    39  
    40  	// Used to save and match topic, it is thread-safe tree.
    41  	tree *topic.Tree
    42  
    43  	// A server accepts incoming connections.
    44  	server transport.Server
    45  
    46  	// A MemoryBackend stores all in memory.
    47  	backend *broker.MemoryBackend
    48  
    49  	// Qos has three types: QOSAtMostOnce, QOSAtLeastOnce, QOSExactlyOnce.
    50  	// now we use QOSAtMostOnce as default.
    51  	qos int
    52  
    53  	// If set retain to true, the topic message will be saved in memory and
    54  	// the future subscribers will receive the msg whose subscriptions match
    55  	// its topic.
    56  	// If set retain to false, then will do nothing.
    57  	retain bool
    58  
    59  	// A sessionQueueSize will default to 100
    60  	sessionQueueSize int
    61  }
    62  
    63  // NewMqttServer create an internal mqtt server.
    64  func NewMqttServer(sqz int, url string, retain bool, qos int) *Server {
    65  	return &Server{
    66  		sessionQueueSize: sqz,
    67  		url:              url,
    68  		tree:             topic.NewTree(),
    69  		retain:           retain,
    70  		qos:              qos,
    71  	}
    72  }
    73  
    74  // Run launch a server and accept connections.
    75  func (m *Server) Run() error {
    76  	var err error
    77  
    78  	m.server, err = transport.Launch(m.url)
    79  	if err != nil {
    80  		klog.Errorf("Launch transport failed %v", err)
    81  		return err
    82  	}
    83  
    84  	m.backend = broker.NewMemoryBackend()
    85  	m.backend.SessionQueueSize = m.sessionQueueSize
    86  
    87  	m.backend.Logger = func(event broker.LogEvent, client *broker.Client, pkt packet.Generic, msg *packet.Message, err error) {
    88  		if event == broker.MessagePublished {
    89  			if len(m.tree.Match(msg.Topic)) > 0 {
    90  				m.onSubscribe(msg)
    91  			}
    92  		}
    93  	}
    94  
    95  	engine := broker.NewEngine(m.backend)
    96  	engine.Accept(m.server)
    97  
    98  	return nil
    99  }
   100  
   101  // onSubscribe will be called if the topic is matched in topic tree.
   102  func (m *Server) onSubscribe(msg *packet.Message) {
   103  	// for "$hw/events/device/+/twin/+", "$hw/events/node/+/membership/get", send to twin
   104  	// for other, send to hub
   105  	// for "SYS/dis/upload_records", no need to base64 topic
   106  	var target string
   107  	resource := base64.URLEncoding.EncodeToString([]byte(msg.Topic))
   108  	if strings.HasPrefix(msg.Topic, "$hw/events/device") || strings.HasPrefix(msg.Topic, "$hw/events/node") {
   109  		target = modules.TwinGroup
   110  	} else {
   111  		target = modules.HubGroup
   112  		if msg.Topic == "SYS/dis/upload_records" {
   113  			resource = "SYS/dis/upload_records"
   114  		}
   115  	}
   116  	// routing key will be $hw.<project_id>.events.user.bus.response.cluster.<cluster_id>.node.<node_id>.<base64_topic>
   117  	message := model.NewMessage("").BuildRouter(modules.BusGroup, "user",
   118  		resource, "response").FillBody(string(msg.Payload))
   119  	klog.Info(fmt.Sprintf("Received msg from mqttserver, deliver to %s with resource %s", target, resource))
   120  	beehiveContext.SendToGroup(target, *message)
   121  }
   122  
   123  // InitInternalTopics sets internal topics to server by default.
   124  func (m *Server) InitInternalTopics() {
   125  	for _, v := range SubTopics {
   126  		m.tree.Set(v, packet.Subscription{Topic: v, QOS: packet.QOS(m.qos)})
   127  		klog.Infof("Subscribe internal topic to %s", v)
   128  	}
   129  }
   130  
   131  // SetTopic set the topic to internal mqtt broker.
   132  func (m *Server) SetTopic(topic string) {
   133  	m.tree.Set(topic, packet.Subscription{Topic: topic, QOS: packet.QOSAtMostOnce})
   134  }
   135  
   136  // Publish will dispatch topic msg to its subscribers directly.
   137  func (m *Server) Publish(topic string, payload []byte) {
   138  	client := &broker.Client{}
   139  
   140  	msg := &packet.Message{
   141  		Topic:   topic,
   142  		Retain:  m.retain,
   143  		Payload: payload,
   144  		QOS:     packet.QOS(m.qos),
   145  	}
   146  	m.backend.Publish(client, msg, nil)
   147  }