micro/broker/http_broker.go

699 lines
13 KiB
Go
Raw Normal View History

package broker
import (
"bytes"
2018-03-03 14:53:52 +03:00
"context"
2016-01-17 01:13:02 +03:00
"crypto/tls"
2017-10-26 22:48:11 +03:00
"errors"
"fmt"
2015-12-23 23:05:47 +03:00
"io"
"io/ioutil"
2015-12-23 23:05:47 +03:00
"math/rand"
"net"
"net/http"
2016-01-17 01:13:02 +03:00
"net/url"
"runtime"
"sync"
2015-12-23 23:05:47 +03:00
"time"
"github.com/google/uuid"
2019-01-10 12:42:02 +03:00
"github.com/micro/go-micro/codec/json"
2017-10-26 22:48:11 +03:00
merr "github.com/micro/go-micro/errors"
2015-11-20 19:17:33 +03:00
"github.com/micro/go-micro/registry"
2019-06-03 17:56:22 +03:00
"github.com/micro/go-micro/registry/cache"
2019-05-31 01:52:10 +03:00
maddr "github.com/micro/go-micro/util/addr"
mnet "github.com/micro/go-micro/util/net"
mls "github.com/micro/go-micro/util/tls"
2018-11-29 15:10:33 +03:00
"golang.org/x/net/http2"
)
2017-10-26 22:48:11 +03:00
// HTTP Broker is a point to point async broker
type httpBroker struct {
2017-10-26 22:48:11 +03:00
id string
address string
opts Options
mux *http.ServeMux
2017-10-28 15:55:59 +03:00
c *http.Client
r registry.Registry
2016-01-17 01:13:02 +03:00
sync.RWMutex
subscribers map[string][]*httpSubscriber
running bool
exit chan chan error
2019-01-02 22:27:46 +03:00
// offline message inbox
mtx sync.RWMutex
inbox map[string][][]byte
}
type httpSubscriber struct {
2015-12-23 22:07:26 +03:00
opts SubscribeOptions
id string
topic string
fn Handler
svc *registry.Service
2017-10-26 22:48:11 +03:00
hb *httpBroker
}
2019-07-07 14:44:09 +03:00
type httpEvent struct {
2015-12-23 22:07:26 +03:00
m *Message
t string
}
var (
2015-12-23 23:05:47 +03:00
DefaultSubPath = "/_sub"
serviceName = "go.micro.http.broker"
2015-12-23 23:05:47 +03:00
broadcastVersion = "ff.http.broadcast"
registerTTL = time.Minute
registerInterval = time.Second * 30
)
2015-12-23 23:05:47 +03:00
func init() {
rand.Seed(time.Now().Unix())
}
func newTransport(config *tls.Config) *http.Transport {
if config == nil {
config = &tls.Config{
InsecureSkipVerify: true,
}
}
2018-11-29 15:10:33 +03:00
dialTLS := func(network string, addr string) (net.Conn, error) {
return tls.Dial(network, addr, config)
}
2016-01-17 01:13:02 +03:00
t := &http.Transport{
Proxy: http.ProxyFromEnvironment,
Dial: (&net.Dialer{
Timeout: 30 * time.Second,
KeepAlive: 30 * time.Second,
}).Dial,
TLSHandshakeTimeout: 10 * time.Second,
2018-11-29 15:10:33 +03:00
DialTLS: dialTLS,
2016-01-17 01:13:02 +03:00
}
runtime.SetFinalizer(&t, func(tr **http.Transport) {
(*tr).CloseIdleConnections()
})
2018-11-29 15:10:33 +03:00
// setup http2
http2.ConfigureTransport(t)
2016-01-17 01:13:02 +03:00
return t
}
2016-03-16 01:12:28 +03:00
func newHttpBroker(opts ...Option) Broker {
2016-01-20 18:22:44 +03:00
options := Options{
Codec: json.Marshaler{},
Context: context.TODO(),
Registry: registry.DefaultRegistry,
2016-01-20 18:22:44 +03:00
}
for _, o := range opts {
o(&options)
}
2017-10-26 22:48:11 +03:00
// set address
addr := ":0"
2016-03-16 01:12:28 +03:00
if len(options.Addrs) > 0 && len(options.Addrs[0]) > 0 {
addr = options.Addrs[0]
}
h := &httpBroker{
id: uuid.New().String(),
address: addr,
opts: options,
r: options.Registry,
c: &http.Client{Transport: newTransport(options.TLSConfig)},
subscribers: make(map[string][]*httpSubscriber),
exit: make(chan chan error),
mux: http.NewServeMux(),
2019-01-02 22:27:46 +03:00
inbox: make(map[string][][]byte),
}
2018-11-18 23:40:43 +03:00
// specify the message handler
h.mux.Handle(DefaultSubPath, h)
2018-11-18 23:40:43 +03:00
// get optional handlers
if h.opts.Context != nil {
handlers, ok := h.opts.Context.Value("http_handlers").(map[string]http.Handler)
if ok {
for pattern, handler := range handlers {
h.mux.Handle(pattern, handler)
}
}
}
return h
}
2019-07-07 14:44:09 +03:00
func (h *httpEvent) Ack() error {
2015-12-23 22:07:26 +03:00
return nil
}
2019-07-07 14:44:09 +03:00
func (h *httpEvent) Message() *Message {
2015-12-23 22:07:26 +03:00
return h.m
}
2019-07-07 14:44:09 +03:00
func (h *httpEvent) Topic() string {
2015-12-23 22:07:26 +03:00
return h.t
}
func (h *httpSubscriber) Options() SubscribeOptions {
2015-12-23 22:07:26 +03:00
return h.opts
}
func (h *httpSubscriber) Topic() string {
return h.topic
}
func (h *httpSubscriber) Unsubscribe() error {
2017-10-26 22:48:11 +03:00
return h.hb.unsubscribe(h)
}
2019-01-02 22:27:46 +03:00
func (h *httpBroker) saveMessage(topic string, msg []byte) {
h.mtx.Lock()
defer h.mtx.Unlock()
// get messages
c := h.inbox[topic]
// save message
c = append(c, msg)
// max length 64
if len(c) > 64 {
c = c[:64]
}
// save inbox
h.inbox[topic] = c
}
func (h *httpBroker) getMessage(topic string, num int) [][]byte {
h.mtx.Lock()
defer h.mtx.Unlock()
// get messages
c, ok := h.inbox[topic]
if !ok {
return nil
}
// more message than requests
if len(c) >= num {
msg := c[:num]
h.inbox[topic] = c[num:]
return msg
}
// reset inbox
h.inbox[topic] = nil
// return all messages
return c
}
2017-10-26 22:48:11 +03:00
func (h *httpBroker) subscribe(s *httpSubscriber) error {
h.Lock()
defer h.Unlock()
if err := h.r.Register(s.svc, registry.RegisterTTL(registerTTL)); err != nil {
return err
}
h.subscribers[s.topic] = append(h.subscribers[s.topic], s)
return nil
}
func (h *httpBroker) unsubscribe(s *httpSubscriber) error {
h.Lock()
defer h.Unlock()
//nolint:prealloc
2017-10-26 22:48:11 +03:00
var subscribers []*httpSubscriber
// look for subscriber
for _, sub := range h.subscribers[s.topic] {
// deregister and skip forward
if sub == s {
2018-11-13 11:56:21 +03:00
_ = h.r.Deregister(sub.svc)
2017-10-26 22:48:11 +03:00
continue
}
// keep subscriber
subscribers = append(subscribers, sub)
}
// set subscribers
h.subscribers[s.topic] = subscribers
return nil
}
func (h *httpBroker) run(l net.Listener) {
t := time.NewTicker(registerInterval)
defer t.Stop()
for {
select {
// heartbeat for each subscriber
case <-t.C:
h.RLock()
for _, subs := range h.subscribers {
for _, sub := range subs {
2018-11-13 11:56:21 +03:00
_ = h.r.Register(sub.svc, registry.RegisterTTL(registerTTL))
}
}
h.RUnlock()
// received exit signal
case ch := <-h.exit:
ch <- l.Close()
2017-10-26 22:48:11 +03:00
h.RLock()
for _, subs := range h.subscribers {
for _, sub := range subs {
2018-11-13 11:56:21 +03:00
_ = h.r.Deregister(sub.svc)
}
}
2017-10-26 22:48:11 +03:00
h.RUnlock()
return
}
}
}
func (h *httpBroker) ServeHTTP(w http.ResponseWriter, req *http.Request) {
if req.Method != "POST" {
err := merr.BadRequest("go.micro.broker", "Method not allowed")
http.Error(w, err.Error(), http.StatusMethodNotAllowed)
return
}
defer req.Body.Close()
req.ParseForm()
b, err := ioutil.ReadAll(req.Body)
if err != nil {
errr := merr.InternalServerError("go.micro.broker", "Error reading request body: %v", err)
w.WriteHeader(500)
w.Write([]byte(errr.Error()))
return
}
var m *Message
if err = h.opts.Codec.Unmarshal(b, &m); err != nil {
errr := merr.InternalServerError("go.micro.broker", "Error parsing request body: %v", err)
w.WriteHeader(500)
w.Write([]byte(errr.Error()))
return
}
topic := m.Header[":topic"]
delete(m.Header, ":topic")
if len(topic) == 0 {
errr := merr.InternalServerError("go.micro.broker", "Topic not found")
w.WriteHeader(500)
w.Write([]byte(errr.Error()))
return
}
2019-07-07 14:44:09 +03:00
p := &httpEvent{m: m, t: topic}
2017-10-26 22:48:11 +03:00
id := req.Form.Get("id")
//nolint:prealloc
2019-10-04 18:30:03 +03:00
var subs []Handler
2017-10-26 22:48:11 +03:00
h.RLock()
for _, subscriber := range h.subscribers[topic] {
2019-10-04 18:30:03 +03:00
if id != subscriber.id {
continue
}
2019-10-04 18:30:03 +03:00
subs = append(subs, subscriber.fn)
}
2017-10-26 22:48:11 +03:00
h.RUnlock()
2019-10-04 18:30:03 +03:00
// execute the handler
for _, fn := range subs {
fn(p)
}
2017-10-26 22:48:11 +03:00
}
func (h *httpBroker) Address() string {
h.RLock()
defer h.RUnlock()
return h.address
}
2017-10-26 22:48:11 +03:00
func (h *httpBroker) Connect() error {
h.RLock()
if h.running {
h.RUnlock()
return nil
}
h.RUnlock()
h.Lock()
defer h.Unlock()
var l net.Listener
var err error
if h.opts.Secure || h.opts.TLSConfig != nil {
2016-01-17 02:39:47 +03:00
config := h.opts.TLSConfig
fn := func(addr string) (net.Listener, error) {
if config == nil {
hosts := []string{addr}
// check if its a valid host:port
if host, _, err := net.SplitHostPort(addr); err == nil {
if len(host) == 0 {
hosts = maddr.IPs()
} else {
hosts = []string{host}
}
}
// generate a certificate
cert, err := mls.Certificate(hosts...)
if err != nil {
return nil, err
}
config = &tls.Config{Certificates: []tls.Certificate{cert}}
2016-01-17 02:39:47 +03:00
}
return tls.Listen("tcp", addr, config)
}
l, err = mnet.Listen(h.address, fn)
} else {
fn := func(addr string) (net.Listener, error) {
return net.Listen("tcp", addr)
}
l, err = mnet.Listen(h.address, fn)
}
if err != nil {
return err
}
addr := h.address
h.address = l.Addr().String()
go http.Serve(l, h.mux)
go func() {
h.run(l)
h.Lock()
2019-07-07 14:33:47 +03:00
h.opts.Addrs = []string{addr}
h.address = addr
h.Unlock()
}()
2017-10-26 22:48:11 +03:00
// get registry
reg, ok := h.opts.Context.Value(registryKey).(registry.Registry)
if !ok {
reg = registry.DefaultRegistry
}
2019-05-31 18:19:31 +03:00
// set cache
h.r = cache.New(reg)
2017-10-26 22:48:11 +03:00
// set running
h.running = true
return nil
}
2017-10-26 22:48:11 +03:00
func (h *httpBroker) Disconnect() error {
h.RLock()
if !h.running {
h.RUnlock()
return nil
}
h.RUnlock()
h.Lock()
defer h.Unlock()
2019-05-31 18:19:31 +03:00
// stop cache
rc, ok := h.r.(cache.Cache)
2017-10-26 22:48:11 +03:00
if ok {
rc.Stop()
}
// exit and return err
ch := make(chan error)
h.exit <- ch
err := <-ch
2017-10-26 22:48:11 +03:00
// set not running
h.running = false
return err
}
2017-10-26 22:48:11 +03:00
func (h *httpBroker) Init(opts ...Option) error {
h.RLock()
2017-10-26 22:48:11 +03:00
if h.running {
h.RUnlock()
2017-10-26 22:48:11 +03:00
return errors.New("cannot init while connected")
}
h.RUnlock()
h.Lock()
defer h.Unlock()
for _, o := range opts {
2016-01-17 02:10:14 +03:00
o(&h.opts)
}
2018-08-18 19:28:58 +03:00
if len(h.opts.Addrs) > 0 && len(h.opts.Addrs[0]) > 0 {
h.address = h.opts.Addrs[0]
}
if len(h.id) == 0 {
h.id = "go.micro.http.broker-" + uuid.New().String()
}
2017-10-26 22:48:11 +03:00
// get registry
2016-01-20 18:22:44 +03:00
reg, ok := h.opts.Context.Value(registryKey).(registry.Registry)
if !ok {
reg = registry.DefaultRegistry
}
2019-05-31 18:19:31 +03:00
// get cache
if rc, ok := h.r.(cache.Cache); ok {
2017-10-26 22:48:11 +03:00
rc.Stop()
}
// set registry
2019-05-31 18:19:31 +03:00
h.r = cache.New(reg)
2016-01-20 18:22:44 +03:00
2018-11-29 15:10:33 +03:00
// reconfigure tls config
if c := h.opts.TLSConfig; c != nil {
h.c = &http.Client{
Transport: newTransport(c),
}
}
return nil
}
func (h *httpBroker) Options() Options {
return h.opts
}
2015-12-23 22:07:26 +03:00
func (h *httpBroker) Publish(topic string, msg *Message, opts ...PublishOption) error {
2019-01-02 22:27:46 +03:00
// create the message first
m := &Message{
Header: make(map[string]string),
Body: msg.Body,
}
for k, v := range msg.Header {
m.Header[k] = v
2016-02-22 02:52:08 +03:00
}
m.Header[":topic"] = topic
2019-01-02 22:27:46 +03:00
// encode the message
b, err := h.opts.Codec.Marshal(m)
if err != nil {
return err
}
2019-01-02 22:27:46 +03:00
// save the message
h.saveMessage(topic, b)
// now attempt to get the service
h.RLock()
s, err := h.r.GetService(serviceName)
2019-01-02 22:27:46 +03:00
if err != nil {
h.RUnlock()
return err
2019-01-02 22:27:46 +03:00
}
h.RUnlock()
2019-01-03 14:23:06 +03:00
pub := func(node *registry.Node, t string, b []byte) error {
2016-01-17 01:13:02 +03:00
scheme := "http"
2016-01-17 01:13:02 +03:00
// check if secure is added in metadata
if node.Metadata["secure"] == "true" {
scheme = "https"
}
vals := url.Values{}
vals.Add("id", node.Id)
2019-07-08 10:01:42 +03:00
uri := fmt.Sprintf("%s://%s%s?%s", scheme, node.Address, DefaultSubPath, vals.Encode())
r, err := h.c.Post(uri, "application/json", bytes.NewReader(b))
2019-01-02 22:27:46 +03:00
if err != nil {
2019-01-03 14:23:06 +03:00
return err
2015-12-23 23:05:47 +03:00
}
2019-01-02 22:27:46 +03:00
// discard response body
io.Copy(ioutil.Discard, r.Body)
r.Body.Close()
2019-01-03 14:23:06 +03:00
return nil
2015-12-23 23:05:47 +03:00
}
2019-01-02 22:27:46 +03:00
srv := func(s []*registry.Service, b []byte) {
for _, service := range s {
var nodes []*registry.Node
for _, node := range service.Nodes {
// only use nodes tagged with broker http
if node.Metadata["broker"] != "http" {
continue
}
// look for nodes for the topic
if node.Metadata["topic"] != topic {
continue
}
nodes = append(nodes, node)
}
2019-10-19 10:11:05 +03:00
// only process if we have nodes
if len(nodes) == 0 {
continue
}
2019-01-02 22:27:46 +03:00
switch service.Version {
// broadcast version means broadcast to all nodes
case broadcastVersion:
2019-01-03 14:23:06 +03:00
var success bool
// publish to all nodes
for _, node := range nodes {
2019-01-02 22:27:46 +03:00
// publish async
2019-01-03 14:23:06 +03:00
if err := pub(node, topic, b); err == nil {
success = true
}
}
// save if it failed to publish at least once
if !success {
h.saveMessage(topic, b)
2019-01-02 22:27:46 +03:00
}
default:
// select node to publish to
node := nodes[rand.Int()%len(nodes)]
2019-01-03 14:23:06 +03:00
// publish async to one node
if err := pub(node, topic, b); err != nil {
// if failed save it
h.saveMessage(topic, b)
}
2015-11-08 04:48:48 +03:00
}
}
}
2015-12-23 23:05:47 +03:00
2019-01-02 22:27:46 +03:00
// do the rest async
go func() {
// get a third of the backlog
messages := h.getMessage(topic, 8)
delay := (len(messages) > 1)
// publish all the messages
for _, msg := range messages {
// serialize here
srv(s, msg)
// sending a backlog of messages
if delay {
time.Sleep(time.Millisecond * 100)
}
}
}()
return nil
}
2015-12-23 22:07:26 +03:00
func (h *httpBroker) Subscribe(topic string, handler Handler, opts ...SubscribeOption) (Subscriber, error) {
var err error
var host, port string
2018-11-30 20:32:48 +03:00
options := NewSubscribeOptions(opts...)
2015-12-23 22:07:26 +03:00
// parse address for host, port
host, port, err = net.SplitHostPort(h.Address())
if err != nil {
return nil, err
}
addr, err := maddr.Extract(host)
2016-02-16 00:57:17 +03:00
if err != nil {
return nil, err
}
var secure bool
if h.opts.Secure || h.opts.TLSConfig != nil {
secure = true
}
// register service
node := &registry.Node{
Id: topic + "-" + h.id,
Address: mnet.HostPort(addr, port),
2016-01-17 01:13:02 +03:00
Metadata: map[string]string{
"secure": fmt.Sprintf("%t", secure),
"broker": "http",
"topic": topic,
2016-01-17 01:13:02 +03:00
},
}
2017-10-26 22:48:11 +03:00
// check for queue group or broadcast queue
version := options.Queue
2015-12-23 23:05:47 +03:00
if len(version) == 0 {
version = broadcastVersion
}
service := &registry.Service{
Name: serviceName,
2015-12-23 23:05:47 +03:00
Version: version,
Nodes: []*registry.Node{node},
}
2017-10-26 22:48:11 +03:00
// generate subscriber
subscriber := &httpSubscriber{
2017-10-26 22:48:11 +03:00
opts: options,
hb: h,
id: node.Id,
topic: topic,
fn: handler,
svc: service,
}
2017-10-26 22:48:11 +03:00
// subscribe now
if err := h.subscribe(subscriber); err != nil {
return nil, err
}
2017-10-26 22:48:11 +03:00
// return the subscriber
return subscriber, nil
}
2015-12-20 00:56:14 +03:00
func (h *httpBroker) String() string {
return "http"
}