netchan.go 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100
  1. // Copyright 2013-2014 Apcera Inc. All rights reserved.
  2. package nats
  3. import (
  4. "errors"
  5. "reflect"
  6. )
  7. // This allows the functionality for network channels by binding send and receive Go chans
  8. // to subjects and optionally queue groups.
  9. // Data will be encoded and decoded via the EncodedConn and its associated encoders.
  10. // BindSendChan binds a channel for send operations to NATS.
  11. func (c *EncodedConn) BindSendChan(subject string, channel interface{}) error {
  12. chVal := reflect.ValueOf(channel)
  13. if chVal.Kind() != reflect.Chan {
  14. return ErrChanArg
  15. }
  16. go chPublish(c, chVal, subject)
  17. return nil
  18. }
  19. // Publish all values that arrive on the channel until it is closed or we
  20. // encounter an error.
  21. func chPublish(c *EncodedConn, chVal reflect.Value, subject string) {
  22. for {
  23. val, ok := chVal.Recv()
  24. if !ok {
  25. // Channel has most likely been closed.
  26. return
  27. }
  28. if e := c.Publish(subject, val.Interface()); e != nil {
  29. // Do this under lock.
  30. c.Conn.mu.Lock()
  31. defer c.Conn.mu.Unlock()
  32. if c.Conn.Opts.AsyncErrorCB != nil {
  33. // FIXME(dlc) - Not sure this is the right thing to do.
  34. // FIXME(ivan) - If the connection is not yet closed, try to schedule the callback
  35. if c.Conn.isClosed() {
  36. go c.Conn.Opts.AsyncErrorCB(c.Conn, nil, e)
  37. } else {
  38. c.Conn.ach <- func() { c.Conn.Opts.AsyncErrorCB(c.Conn, nil, e) }
  39. }
  40. }
  41. return
  42. }
  43. }
  44. }
  45. // BindRecvChan binds a channel for receive operations from NATS.
  46. func (c *EncodedConn) BindRecvChan(subject string, channel interface{}) (*Subscription, error) {
  47. return c.bindRecvChan(subject, _EMPTY_, channel)
  48. }
  49. // BindRecvQueueChan binds a channel for queue-based receive operations from NATS.
  50. func (c *EncodedConn) BindRecvQueueChan(subject, queue string, channel interface{}) (*Subscription, error) {
  51. return c.bindRecvChan(subject, queue, channel)
  52. }
  53. // Internal function to bind receive operations for a channel.
  54. func (c *EncodedConn) bindRecvChan(subject, queue string, channel interface{}) (*Subscription, error) {
  55. chVal := reflect.ValueOf(channel)
  56. if chVal.Kind() != reflect.Chan {
  57. return nil, ErrChanArg
  58. }
  59. argType := chVal.Type().Elem()
  60. cb := func(m *Msg) {
  61. var oPtr reflect.Value
  62. if argType.Kind() != reflect.Ptr {
  63. oPtr = reflect.New(argType)
  64. } else {
  65. oPtr = reflect.New(argType.Elem())
  66. }
  67. if err := c.Enc.Decode(m.Subject, m.Data, oPtr.Interface()); err != nil {
  68. c.Conn.err = errors.New("nats: Got an error trying to unmarshal: " + err.Error())
  69. if c.Conn.Opts.AsyncErrorCB != nil {
  70. c.Conn.ach <- func() { c.Conn.Opts.AsyncErrorCB(c.Conn, m.Sub, c.Conn.err) }
  71. }
  72. return
  73. }
  74. if argType.Kind() != reflect.Ptr {
  75. oPtr = reflect.Indirect(oPtr)
  76. }
  77. // This is a bit hacky, but in this instance we may be trying to send to a closed channel.
  78. // and the user does not know when it is safe to close the channel.
  79. defer func() {
  80. // If we have panicked, recover and close the subscription.
  81. if r := recover(); r != nil {
  82. m.Sub.Unsubscribe()
  83. }
  84. }()
  85. // Actually do the send to the channel.
  86. chVal.Send(oPtr)
  87. }
  88. return c.Conn.subscribe(subject, queue, cb, nil)
  89. }