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