gitee.com/liuxuezhan/go-micro-v1.18.0@v1.0.0/broker/http_broker.go (about)

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