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 := ®istry.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 := ®istry.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 }