uds.go 2.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100
  1. // +build !windows
  2. package statsd
  3. import (
  4. "net"
  5. "sync"
  6. "time"
  7. )
  8. /*
  9. UDSTimeout holds the default timeout for UDS socket writes, as they can get
  10. blocking when the receiving buffer is full.
  11. */
  12. const defaultUDSTimeout = 100 * time.Millisecond
  13. // udsWriter is an internal class wrapping around management of UDS connection
  14. type udsWriter struct {
  15. // Address to send metrics to, needed to allow reconnection on error
  16. addr net.Addr
  17. // Established connection object, or nil if not connected yet
  18. conn net.Conn
  19. // write timeout
  20. writeTimeout time.Duration
  21. sync.RWMutex // used to lock conn / writer can replace it
  22. }
  23. // newUDSWriter returns a pointer to a new udsWriter given a socket file path as addr.
  24. func newUDSWriter(addr string) (*udsWriter, error) {
  25. udsAddr, err := net.ResolveUnixAddr("unixgram", addr)
  26. if err != nil {
  27. return nil, err
  28. }
  29. // Defer connection to first Write
  30. writer := &udsWriter{addr: udsAddr, conn: nil, writeTimeout: defaultUDSTimeout}
  31. return writer, nil
  32. }
  33. // SetWriteTimeout allows the user to set a custom write timeout
  34. func (w *udsWriter) SetWriteTimeout(d time.Duration) error {
  35. w.writeTimeout = d
  36. return nil
  37. }
  38. // Write data to the UDS connection with write timeout and minimal error handling:
  39. // create the connection if nil, and destroy it if the statsd server has disconnected
  40. func (w *udsWriter) Write(data []byte) (int, error) {
  41. conn, err := w.ensureConnection()
  42. if err != nil {
  43. return 0, err
  44. }
  45. conn.SetWriteDeadline(time.Now().Add(w.writeTimeout))
  46. n, e := conn.Write(data)
  47. if err, isNetworkErr := e.(net.Error); err != nil && (!isNetworkErr || !err.Temporary()) {
  48. // Statsd server disconnected, retry connecting at next packet
  49. w.unsetConnection()
  50. return 0, e
  51. }
  52. return n, e
  53. }
  54. func (w *udsWriter) Close() error {
  55. if w.conn != nil {
  56. return w.conn.Close()
  57. }
  58. return nil
  59. }
  60. func (w *udsWriter) ensureConnection() (net.Conn, error) {
  61. // Check if we've already got a socket we can use
  62. w.RLock()
  63. currentConn := w.conn
  64. w.RUnlock()
  65. if currentConn != nil {
  66. return currentConn, nil
  67. }
  68. // Looks like we might need to connect - try again with write locking.
  69. w.Lock()
  70. defer w.Unlock()
  71. if w.conn != nil {
  72. return w.conn, nil
  73. }
  74. newConn, err := net.Dial(w.addr.Network(), w.addr.String())
  75. if err != nil {
  76. return nil, err
  77. }
  78. w.conn = newConn
  79. return newConn, nil
  80. }
  81. func (w *udsWriter) unsetConnection() {
  82. w.Lock()
  83. defer w.Unlock()
  84. w.conn = nil
  85. }