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