gitee.com/sasukebo/go-micro/v4@v4.7.1/broker/http.go (about)

     1  // Package http provides a http based message broker
     2  package broker
     3  
     4  import (
     5  	"bytes"
     6  	"context"
     7  	"crypto/tls"
     8  	"errors"
     9  	"fmt"
    10  	"io"
    11  	"math/rand"
    12  	"net"
    13  	"net/http"
    14  	"net/url"
    15  	"runtime"
    16  	"sync"
    17  	"time"
    18  
    19  	"gitee.com/sasukebo/go-micro/v4/codec/json"
    20  	merr "gitee.com/sasukebo/go-micro/v4/errors"
    21  	"gitee.com/sasukebo/go-micro/v4/registry"
    22  	"gitee.com/sasukebo/go-micro/v4/registry/cache"
    23  	maddr "gitee.com/sasukebo/go-micro/v4/util/addr"
    24  	mnet "gitee.com/sasukebo/go-micro/v4/util/net"
    25  	mls "gitee.com/sasukebo/go-micro/v4/util/tls"
    26  	"github.com/google/uuid"
    27  	"golang.org/x/net/http2"
    28  )
    29  
    30  // HTTP Broker is a point to point async broker
    31  type httpBroker struct {
    32  	id      string
    33  	address string
    34  	opts    Options
    35  
    36  	mux *http.ServeMux
    37  
    38  	c *http.Client
    39  	r registry.Registry
    40  
    41  	sync.RWMutex
    42  	subscribers map[string][]*httpSubscriber
    43  	running     bool
    44  	exit        chan chan error
    45  
    46  	// offline message inbox
    47  	mtx   sync.RWMutex
    48  	inbox map[string][][]byte
    49  }
    50  
    51  type httpSubscriber struct {
    52  	opts  SubscribeOptions
    53  	id    string
    54  	topic string
    55  	fn    Handler
    56  	svc   *registry.Service
    57  	hb    *httpBroker
    58  }
    59  
    60  type httpEvent struct {
    61  	m   *Message
    62  	t   string
    63  	err error
    64  }
    65  
    66  var (
    67  	DefaultPath      = "/"
    68  	DefaultAddress   = "127.0.0.1:0"
    69  	serviceName      = "micro.http.broker"
    70  	broadcastVersion = "ff.http.broadcast"
    71  	registerTTL      = time.Minute
    72  	registerInterval = time.Second * 30
    73  )
    74  
    75  func init() {
    76  	rand.Seed(time.Now().Unix())
    77  }
    78  
    79  func newTransport(config *tls.Config) *http.Transport {
    80  	if config == nil {
    81  		config = &tls.Config{
    82  			InsecureSkipVerify: true,
    83  		}
    84  	}
    85  
    86  	dialTLS := func(network string, addr string) (net.Conn, error) {
    87  		return tls.Dial(network, addr, config)
    88  	}
    89  
    90  	t := &http.Transport{
    91  		Proxy: http.ProxyFromEnvironment,
    92  		Dial: (&net.Dialer{
    93  			Timeout:   30 * time.Second,
    94  			KeepAlive: 30 * time.Second,
    95  		}).Dial,
    96  		TLSHandshakeTimeout: 10 * time.Second,
    97  		DialTLS:             dialTLS,
    98  	}
    99  	runtime.SetFinalizer(&t, func(tr **http.Transport) {
   100  		(*tr).CloseIdleConnections()
   101  	})
   102  
   103  	// setup http2
   104  	http2.ConfigureTransport(t)
   105  
   106  	return t
   107  }
   108  
   109  func newHttpBroker(opts ...Option) Broker {
   110  	options := Options{
   111  		Codec:    json.Marshaler{},
   112  		Context:  context.TODO(),
   113  		Registry: registry.DefaultRegistry,
   114  	}
   115  
   116  	for _, o := range opts {
   117  		o(&options)
   118  	}
   119  
   120  	// set address
   121  	addr := DefaultAddress
   122  
   123  	if len(options.Addrs) > 0 && len(options.Addrs[0]) > 0 {
   124  		addr = options.Addrs[0]
   125  	}
   126  
   127  	h := &httpBroker{
   128  		id:          uuid.New().String(),
   129  		address:     addr,
   130  		opts:        options,
   131  		r:           options.Registry,
   132  		c:           &http.Client{Transport: newTransport(options.TLSConfig)},
   133  		subscribers: make(map[string][]*httpSubscriber),
   134  		exit:        make(chan chan error),
   135  		mux:         http.NewServeMux(),
   136  		inbox:       make(map[string][][]byte),
   137  	}
   138  
   139  	// specify the message handler
   140  	h.mux.Handle(DefaultPath, h)
   141  
   142  	// get optional handlers
   143  	if h.opts.Context != nil {
   144  		handlers, ok := h.opts.Context.Value("http_handlers").(map[string]http.Handler)
   145  		if ok {
   146  			for pattern, handler := range handlers {
   147  				h.mux.Handle(pattern, handler)
   148  			}
   149  		}
   150  	}
   151  
   152  	return h
   153  }
   154  
   155  func (h *httpEvent) Ack() error {
   156  	return nil
   157  }
   158  
   159  func (h *httpEvent) Error() error {
   160  	return h.err
   161  }
   162  
   163  func (h *httpEvent) Message() *Message {
   164  	return h.m
   165  }
   166  
   167  func (h *httpEvent) Topic() string {
   168  	return h.t
   169  }
   170  
   171  func (h *httpSubscriber) Options() SubscribeOptions {
   172  	return h.opts
   173  }
   174  
   175  func (h *httpSubscriber) Topic() string {
   176  	return h.topic
   177  }
   178  
   179  func (h *httpSubscriber) Unsubscribe() error {
   180  	return h.hb.unsubscribe(h)
   181  }
   182  
   183  func (h *httpBroker) saveMessage(topic string, msg []byte) {
   184  	h.mtx.Lock()
   185  	defer h.mtx.Unlock()
   186  
   187  	// get messages
   188  	c := h.inbox[topic]
   189  
   190  	// save message
   191  	c = append(c, msg)
   192  
   193  	// max length 64
   194  	if len(c) > 64 {
   195  		c = c[:64]
   196  	}
   197  
   198  	// save inbox
   199  	h.inbox[topic] = c
   200  }
   201  
   202  func (h *httpBroker) getMessage(topic string, num int) [][]byte {
   203  	h.mtx.Lock()
   204  	defer h.mtx.Unlock()
   205  
   206  	// get messages
   207  	c, ok := h.inbox[topic]
   208  	if !ok {
   209  		return nil
   210  	}
   211  
   212  	// more message than requests
   213  	if len(c) >= num {
   214  		msg := c[:num]
   215  		h.inbox[topic] = c[num:]
   216  		return msg
   217  	}
   218  
   219  	// reset inbox
   220  	h.inbox[topic] = nil
   221  
   222  	// return all messages
   223  	return c
   224  }
   225  
   226  func (h *httpBroker) subscribe(s *httpSubscriber) error {
   227  	h.Lock()
   228  	defer h.Unlock()
   229  
   230  	if err := h.r.Register(s.svc, registry.RegisterTTL(registerTTL)); err != nil {
   231  		return err
   232  	}
   233  
   234  	h.subscribers[s.topic] = append(h.subscribers[s.topic], s)
   235  	return nil
   236  }
   237  
   238  func (h *httpBroker) unsubscribe(s *httpSubscriber) error {
   239  	h.Lock()
   240  	defer h.Unlock()
   241  
   242  	//nolint:prealloc
   243  	var subscribers []*httpSubscriber
   244  
   245  	// look for subscriber
   246  	for _, sub := range h.subscribers[s.topic] {
   247  		// deregister and skip forward
   248  		if sub == s {
   249  			_ = h.r.Deregister(sub.svc)
   250  			continue
   251  		}
   252  		// keep subscriber
   253  		subscribers = append(subscribers, sub)
   254  	}
   255  
   256  	// set subscribers
   257  	h.subscribers[s.topic] = subscribers
   258  
   259  	return nil
   260  }
   261  
   262  func (h *httpBroker) run(l net.Listener) {
   263  	t := time.NewTicker(registerInterval)
   264  	defer t.Stop()
   265  
   266  	for {
   267  		select {
   268  		// heartbeat for each subscriber
   269  		case <-t.C:
   270  			h.RLock()
   271  			for _, subs := range h.subscribers {
   272  				for _, sub := range subs {
   273  					_ = h.r.Register(sub.svc, registry.RegisterTTL(registerTTL))
   274  				}
   275  			}
   276  			h.RUnlock()
   277  		// received exit signal
   278  		case ch := <-h.exit:
   279  			ch <- l.Close()
   280  			h.RLock()
   281  			for _, subs := range h.subscribers {
   282  				for _, sub := range subs {
   283  					_ = h.r.Deregister(sub.svc)
   284  				}
   285  			}
   286  			h.RUnlock()
   287  			return
   288  		}
   289  	}
   290  }
   291  
   292  func (h *httpBroker) ServeHTTP(w http.ResponseWriter, req *http.Request) {
   293  	if req.Method != "POST" {
   294  		err := merr.BadRequest("go.micro.broker", "Method not allowed")
   295  		http.Error(w, err.Error(), http.StatusMethodNotAllowed)
   296  		return
   297  	}
   298  	defer req.Body.Close()
   299  
   300  	req.ParseForm()
   301  
   302  	b, err := io.ReadAll(req.Body)
   303  	if err != nil {
   304  		errr := merr.InternalServerError("go.micro.broker", "Error reading request body: %v", err)
   305  		w.WriteHeader(500)
   306  		w.Write([]byte(errr.Error()))
   307  		return
   308  	}
   309  
   310  	var m *Message
   311  	if err = h.opts.Codec.Unmarshal(b, &m); err != nil {
   312  		errr := merr.InternalServerError("go.micro.broker", "Error parsing request body: %v", err)
   313  		w.WriteHeader(500)
   314  		w.Write([]byte(errr.Error()))
   315  		return
   316  	}
   317  
   318  	topic := m.Header["Micro-Topic"]
   319  	// delete(m.Header, ":topic")
   320  
   321  	if len(topic) == 0 {
   322  		errr := merr.InternalServerError("go.micro.broker", "Topic not found")
   323  		w.WriteHeader(500)
   324  		w.Write([]byte(errr.Error()))
   325  		return
   326  	}
   327  
   328  	p := &httpEvent{m: m, t: topic}
   329  	id := req.Form.Get("id")
   330  
   331  	//nolint:prealloc
   332  	var subs []Handler
   333  
   334  	h.RLock()
   335  	for _, subscriber := range h.subscribers[topic] {
   336  		if id != subscriber.id {
   337  			continue
   338  		}
   339  		subs = append(subs, subscriber.fn)
   340  	}
   341  	h.RUnlock()
   342  
   343  	// execute the handler
   344  	for _, fn := range subs {
   345  		p.err = fn(p)
   346  	}
   347  }
   348  
   349  func (h *httpBroker) Address() string {
   350  	h.RLock()
   351  	defer h.RUnlock()
   352  	return h.address
   353  }
   354  
   355  func (h *httpBroker) Connect() error {
   356  	h.RLock()
   357  	if h.running {
   358  		h.RUnlock()
   359  		return nil
   360  	}
   361  	h.RUnlock()
   362  
   363  	h.Lock()
   364  	defer h.Unlock()
   365  
   366  	var l net.Listener
   367  	var err error
   368  
   369  	if h.opts.Secure || h.opts.TLSConfig != nil {
   370  		config := h.opts.TLSConfig
   371  
   372  		fn := func(addr string) (net.Listener, error) {
   373  			if config == nil {
   374  				hosts := []string{addr}
   375  
   376  				// check if its a valid host:port
   377  				if host, _, err := net.SplitHostPort(addr); err == nil {
   378  					if len(host) == 0 {
   379  						hosts = maddr.IPs()
   380  					} else {
   381  						hosts = []string{host}
   382  					}
   383  				}
   384  
   385  				// generate a certificate
   386  				cert, err := mls.Certificate(hosts...)
   387  				if err != nil {
   388  					return nil, err
   389  				}
   390  				config = &tls.Config{Certificates: []tls.Certificate{cert}}
   391  			}
   392  			return tls.Listen("tcp", addr, config)
   393  		}
   394  
   395  		l, err = mnet.Listen(h.address, fn)
   396  	} else {
   397  		fn := func(addr string) (net.Listener, error) {
   398  			return net.Listen("tcp", addr)
   399  		}
   400  
   401  		l, err = mnet.Listen(h.address, fn)
   402  	}
   403  
   404  	if err != nil {
   405  		return err
   406  	}
   407  
   408  	addr := h.address
   409  	h.address = l.Addr().String()
   410  
   411  	go http.Serve(l, h.mux)
   412  	go func() {
   413  		h.run(l)
   414  		h.Lock()
   415  		h.opts.Addrs = []string{addr}
   416  		h.address = addr
   417  		h.Unlock()
   418  	}()
   419  
   420  	// get registry
   421  	reg := h.opts.Registry
   422  	if reg == nil {
   423  		reg = registry.DefaultRegistry
   424  	}
   425  	// set cache
   426  	h.r = cache.New(reg)
   427  
   428  	// set running
   429  	h.running = true
   430  	return nil
   431  }
   432  
   433  func (h *httpBroker) Disconnect() error {
   434  	h.RLock()
   435  	if !h.running {
   436  		h.RUnlock()
   437  		return nil
   438  	}
   439  	h.RUnlock()
   440  
   441  	h.Lock()
   442  	defer h.Unlock()
   443  
   444  	// stop cache
   445  	rc, ok := h.r.(cache.Cache)
   446  	if ok {
   447  		rc.Stop()
   448  	}
   449  
   450  	// exit and return err
   451  	ch := make(chan error)
   452  	h.exit <- ch
   453  	err := <-ch
   454  
   455  	// set not running
   456  	h.running = false
   457  	return err
   458  }
   459  
   460  func (h *httpBroker) Init(opts ...Option) error {
   461  	h.RLock()
   462  	if h.running {
   463  		h.RUnlock()
   464  		return errors.New("cannot init while connected")
   465  	}
   466  	h.RUnlock()
   467  
   468  	h.Lock()
   469  	defer h.Unlock()
   470  
   471  	for _, o := range opts {
   472  		o(&h.opts)
   473  	}
   474  
   475  	if len(h.opts.Addrs) > 0 && len(h.opts.Addrs[0]) > 0 {
   476  		h.address = h.opts.Addrs[0]
   477  	}
   478  
   479  	if len(h.id) == 0 {
   480  		h.id = "go.micro.http.broker-" + uuid.New().String()
   481  	}
   482  
   483  	// get registry
   484  	reg := h.opts.Registry
   485  	if reg == nil {
   486  		reg = registry.DefaultRegistry
   487  	}
   488  
   489  	// get cache
   490  	if rc, ok := h.r.(cache.Cache); ok {
   491  		rc.Stop()
   492  	}
   493  
   494  	// set registry
   495  	h.r = cache.New(reg)
   496  
   497  	// reconfigure tls config
   498  	if c := h.opts.TLSConfig; c != nil {
   499  		h.c = &http.Client{
   500  			Transport: newTransport(c),
   501  		}
   502  	}
   503  
   504  	return nil
   505  }
   506  
   507  func (h *httpBroker) Options() Options {
   508  	return h.opts
   509  }
   510  
   511  func (h *httpBroker) Publish(topic string, msg *Message, opts ...PublishOption) error {
   512  	// create the message first
   513  	m := &Message{
   514  		Header: make(map[string]string),
   515  		Body:   msg.Body,
   516  	}
   517  
   518  	for k, v := range msg.Header {
   519  		m.Header[k] = v
   520  	}
   521  
   522  	m.Header["Micro-Topic"] = topic
   523  
   524  	// encode the message
   525  	b, err := h.opts.Codec.Marshal(m)
   526  	if err != nil {
   527  		return err
   528  	}
   529  
   530  	// save the message
   531  	h.saveMessage(topic, b)
   532  
   533  	// now attempt to get the service
   534  	h.RLock()
   535  	s, err := h.r.GetService(serviceName)
   536  	if err != nil {
   537  		h.RUnlock()
   538  		return err
   539  	}
   540  	h.RUnlock()
   541  
   542  	pub := func(node *registry.Node, t string, b []byte) error {
   543  		scheme := "http"
   544  
   545  		// check if secure is added in metadata
   546  		if node.Metadata["secure"] == "true" {
   547  			scheme = "https"
   548  		}
   549  
   550  		vals := url.Values{}
   551  		vals.Add("id", node.Id)
   552  
   553  		uri := fmt.Sprintf("%s://%s%s?%s", scheme, node.Address, DefaultPath, vals.Encode())
   554  		r, err := h.c.Post(uri, "application/json", bytes.NewReader(b))
   555  		if err != nil {
   556  			return err
   557  		}
   558  
   559  		// discard response body
   560  		io.Copy(io.Discard, r.Body)
   561  		r.Body.Close()
   562  		return nil
   563  	}
   564  
   565  	srv := func(s []*registry.Service, b []byte) {
   566  		for _, service := range s {
   567  			var nodes []*registry.Node
   568  
   569  			for _, node := range service.Nodes {
   570  				// only use nodes tagged with broker http
   571  				if node.Metadata["broker"] != "http" {
   572  					continue
   573  				}
   574  
   575  				// look for nodes for the topic
   576  				if node.Metadata["topic"] != topic {
   577  					continue
   578  				}
   579  
   580  				nodes = append(nodes, node)
   581  			}
   582  
   583  			// only process if we have nodes
   584  			if len(nodes) == 0 {
   585  				continue
   586  			}
   587  
   588  			switch service.Version {
   589  			// broadcast version means broadcast to all nodes
   590  			case broadcastVersion:
   591  				var success bool
   592  
   593  				// publish to all nodes
   594  				for _, node := range nodes {
   595  					// publish async
   596  					if err := pub(node, topic, b); err == nil {
   597  						success = true
   598  					}
   599  				}
   600  
   601  				// save if it failed to publish at least once
   602  				if !success {
   603  					h.saveMessage(topic, b)
   604  				}
   605  			default:
   606  				// select node to publish to
   607  				node := nodes[rand.Int()%len(nodes)]
   608  
   609  				// publish async to one node
   610  				if err := pub(node, topic, b); err != nil {
   611  					// if failed save it
   612  					h.saveMessage(topic, b)
   613  				}
   614  			}
   615  		}
   616  	}
   617  
   618  	// do the rest async
   619  	go func() {
   620  		// get a third of the backlog
   621  		messages := h.getMessage(topic, 8)
   622  		delay := (len(messages) > 1)
   623  
   624  		// publish all the messages
   625  		for _, msg := range messages {
   626  			// serialize here
   627  			srv(s, msg)
   628  
   629  			// sending a backlog of messages
   630  			if delay {
   631  				time.Sleep(time.Millisecond * 100)
   632  			}
   633  		}
   634  	}()
   635  
   636  	return nil
   637  }
   638  
   639  func (h *httpBroker) Subscribe(topic string, handler Handler, opts ...SubscribeOption) (Subscriber, error) {
   640  	var err error
   641  	var host, port string
   642  	options := NewSubscribeOptions(opts...)
   643  
   644  	// parse address for host, port
   645  	host, port, err = net.SplitHostPort(h.Address())
   646  	if err != nil {
   647  		return nil, err
   648  	}
   649  
   650  	addr, err := maddr.Extract(host)
   651  	if err != nil {
   652  		return nil, err
   653  	}
   654  
   655  	var secure bool
   656  
   657  	if h.opts.Secure || h.opts.TLSConfig != nil {
   658  		secure = true
   659  	}
   660  
   661  	// register service
   662  	node := &registry.Node{
   663  		Id:      topic + "-" + h.id,
   664  		Address: mnet.HostPort(addr, port),
   665  		Metadata: map[string]string{
   666  			"secure": fmt.Sprintf("%t", secure),
   667  			"broker": "http",
   668  			"topic":  topic,
   669  		},
   670  	}
   671  
   672  	// check for queue group or broadcast queue
   673  	version := options.Queue
   674  	if len(version) == 0 {
   675  		version = broadcastVersion
   676  	}
   677  
   678  	service := &registry.Service{
   679  		Name:    serviceName,
   680  		Version: version,
   681  		Nodes:   []*registry.Node{node},
   682  	}
   683  
   684  	// generate subscriber
   685  	subscriber := &httpSubscriber{
   686  		opts:  options,
   687  		hb:    h,
   688  		id:    node.Id,
   689  		topic: topic,
   690  		fn:    handler,
   691  		svc:   service,
   692  	}
   693  
   694  	// subscribe now
   695  	if err := h.subscribe(subscriber); err != nil {
   696  		return nil, err
   697  	}
   698  
   699  	// return the subscriber
   700  	return subscriber, nil
   701  }
   702  
   703  func (h *httpBroker) String() string {
   704  	return "http"
   705  }
   706  
   707  // NewBroker returns a new http broker
   708  func NewBroker(opts ...Option) Broker {
   709  	return newHttpBroker(opts...)
   710  }