2019-05-22 21:16:55 +00:00
|
|
|
package graphite
|
|
|
|
|
|
|
|
import (
|
2020-06-30 21:15:34 +00:00
|
|
|
"errors"
|
2020-02-23 11:35:47 +00:00
|
|
|
"io"
|
2019-05-22 21:16:55 +00:00
|
|
|
"net"
|
|
|
|
"strings"
|
|
|
|
"sync"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/VictoriaMetrics/VictoriaMetrics/lib/bytesutil"
|
2020-12-08 18:49:32 +00:00
|
|
|
"github.com/VictoriaMetrics/VictoriaMetrics/lib/cgroup"
|
2021-05-08 16:36:00 +00:00
|
|
|
"github.com/VictoriaMetrics/VictoriaMetrics/lib/ingestserver"
|
2019-05-22 21:16:55 +00:00
|
|
|
"github.com/VictoriaMetrics/VictoriaMetrics/lib/logger"
|
2019-07-15 19:58:00 +00:00
|
|
|
"github.com/VictoriaMetrics/VictoriaMetrics/lib/netutil"
|
2019-05-22 21:16:55 +00:00
|
|
|
"github.com/VictoriaMetrics/metrics"
|
|
|
|
)
|
|
|
|
|
|
|
|
var (
|
2020-02-25 17:09:46 +00:00
|
|
|
writeRequestsTCP = metrics.NewCounter(`vm_ingestserver_requests_total{type="graphite", name="write", net="tcp"}`)
|
|
|
|
writeErrorsTCP = metrics.NewCounter(`vm_ingestserver_request_errors_total{type="graphite", name="write", net="tcp"}`)
|
2019-05-22 21:16:55 +00:00
|
|
|
|
2020-02-25 17:09:46 +00:00
|
|
|
writeRequestsUDP = metrics.NewCounter(`vm_ingestserver_requests_total{type="graphite", name="write", net="udp"}`)
|
|
|
|
writeErrorsUDP = metrics.NewCounter(`vm_ingestserver_request_errors_total{type="graphite", name="write", net="udp"}`)
|
2019-05-22 21:16:55 +00:00
|
|
|
)
|
|
|
|
|
2019-12-13 22:29:14 +00:00
|
|
|
// Server accepts Graphite plaintext lines over TCP and UDP.
|
|
|
|
type Server struct {
|
|
|
|
addr string
|
|
|
|
lnTCP net.Listener
|
|
|
|
lnUDP net.PacketConn
|
|
|
|
wg sync.WaitGroup
|
2021-05-08 16:36:00 +00:00
|
|
|
cm ingestserver.ConnsMap
|
2019-12-13 22:29:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// MustStart starts graphite server on the given addr.
|
|
|
|
//
|
2020-02-23 11:35:47 +00:00
|
|
|
// The incoming connections are processed with insertHandler.
|
|
|
|
//
|
2023-01-27 07:08:35 +00:00
|
|
|
// If useProxyProtocol is set to true, then the incoming connections are accepted via proxy protocol.
|
|
|
|
// See https://www.haproxy.org/download/1.8/doc/proxy-protocol.txt
|
|
|
|
//
|
2019-12-13 22:29:14 +00:00
|
|
|
// MustStop must be called on the returned server when it is no longer needed.
|
2023-01-27 07:08:35 +00:00
|
|
|
func MustStart(addr string, useProxyProtocol bool, insertHandler func(r io.Reader) error) *Server {
|
2019-05-22 21:16:55 +00:00
|
|
|
logger.Infof("starting TCP Graphite server at %q", addr)
|
2023-01-27 07:08:35 +00:00
|
|
|
lnTCP, err := netutil.NewTCPListener("graphite", addr, useProxyProtocol, nil)
|
2019-05-22 21:16:55 +00:00
|
|
|
if err != nil {
|
|
|
|
logger.Fatalf("cannot start TCP Graphite server at %q: %s", addr, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
logger.Infof("starting UDP Graphite server at %q", addr)
|
2021-03-16 22:03:06 +00:00
|
|
|
lnUDP, err := net.ListenPacket(netutil.GetUDPNetwork(), addr)
|
2019-05-22 21:16:55 +00:00
|
|
|
if err != nil {
|
|
|
|
logger.Fatalf("cannot start UDP Graphite server at %q: %s", addr, err)
|
|
|
|
}
|
|
|
|
|
2019-12-13 22:29:14 +00:00
|
|
|
s := &Server{
|
|
|
|
addr: addr,
|
|
|
|
lnTCP: lnTCP,
|
|
|
|
lnUDP: lnUDP,
|
|
|
|
}
|
2023-11-14 00:00:42 +00:00
|
|
|
s.cm.Init("graphite")
|
2019-12-13 22:29:14 +00:00
|
|
|
s.wg.Add(1)
|
2019-05-22 21:16:55 +00:00
|
|
|
go func() {
|
2019-12-13 22:29:14 +00:00
|
|
|
defer s.wg.Done()
|
2021-05-08 16:36:00 +00:00
|
|
|
s.serveTCP(insertHandler)
|
2019-05-22 21:16:55 +00:00
|
|
|
logger.Infof("stopped TCP Graphite server at %q", addr)
|
|
|
|
}()
|
2019-12-13 22:29:14 +00:00
|
|
|
s.wg.Add(1)
|
2019-05-22 21:16:55 +00:00
|
|
|
go func() {
|
2019-12-13 22:29:14 +00:00
|
|
|
defer s.wg.Done()
|
2021-05-08 16:36:00 +00:00
|
|
|
s.serveUDP(insertHandler)
|
2019-05-22 21:16:55 +00:00
|
|
|
logger.Infof("stopped UDP Graphite server at %q", addr)
|
|
|
|
}()
|
2019-12-13 22:29:14 +00:00
|
|
|
return s
|
|
|
|
}
|
|
|
|
|
|
|
|
// MustStop stops the server.
|
|
|
|
func (s *Server) MustStop() {
|
|
|
|
logger.Infof("stopping TCP Graphite server at %q...", s.addr)
|
|
|
|
if err := s.lnTCP.Close(); err != nil {
|
|
|
|
logger.Errorf("cannot close TCP Graphite server: %s", err)
|
|
|
|
}
|
|
|
|
logger.Infof("stopping UDP Graphite server at %q...", s.addr)
|
|
|
|
if err := s.lnUDP.Close(); err != nil {
|
|
|
|
logger.Errorf("cannot close UDP Graphite server: %s", err)
|
|
|
|
}
|
2023-11-14 00:00:42 +00:00
|
|
|
s.cm.CloseAll(0)
|
2019-12-13 22:29:14 +00:00
|
|
|
s.wg.Wait()
|
|
|
|
logger.Infof("TCP and UDP Graphite servers at %q have been stopped", s.addr)
|
2019-05-22 21:16:55 +00:00
|
|
|
}
|
|
|
|
|
2021-05-08 16:36:00 +00:00
|
|
|
func (s *Server) serveTCP(insertHandler func(r io.Reader) error) {
|
|
|
|
var wg sync.WaitGroup
|
2019-05-22 21:16:55 +00:00
|
|
|
for {
|
2021-05-08 16:36:00 +00:00
|
|
|
c, err := s.lnTCP.Accept()
|
2019-05-22 21:16:55 +00:00
|
|
|
if err != nil {
|
2020-06-30 21:15:34 +00:00
|
|
|
var ne net.Error
|
|
|
|
if errors.As(err, &ne) {
|
2019-05-22 21:16:55 +00:00
|
|
|
if ne.Temporary() {
|
2021-05-08 16:36:00 +00:00
|
|
|
logger.Errorf("graphite: temporary error when listening for TCP addr %q: %s", s.lnTCP.Addr(), err)
|
2019-05-22 21:16:55 +00:00
|
|
|
time.Sleep(time.Second)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if strings.Contains(err.Error(), "use of closed network connection") {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
logger.Fatalf("unrecoverable error when accepting TCP Graphite connections: %s", err)
|
|
|
|
}
|
|
|
|
logger.Fatalf("unexpected error when accepting TCP Graphite connections: %s", err)
|
|
|
|
}
|
2021-05-08 16:36:00 +00:00
|
|
|
if !s.cm.Add(c) {
|
|
|
|
_ = c.Close()
|
|
|
|
break
|
|
|
|
}
|
|
|
|
wg.Add(1)
|
2019-05-22 21:16:55 +00:00
|
|
|
go func() {
|
2021-05-08 16:36:00 +00:00
|
|
|
defer func() {
|
|
|
|
s.cm.Delete(c)
|
|
|
|
_ = c.Close()
|
|
|
|
wg.Done()
|
|
|
|
}()
|
2019-05-22 21:16:55 +00:00
|
|
|
writeRequestsTCP.Inc()
|
|
|
|
if err := insertHandler(c); err != nil {
|
|
|
|
writeErrorsTCP.Inc()
|
|
|
|
logger.Errorf("error in TCP Graphite conn %q<->%q: %s", c.LocalAddr(), c.RemoteAddr(), err)
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
}
|
2021-05-08 16:36:00 +00:00
|
|
|
wg.Wait()
|
2019-05-22 21:16:55 +00:00
|
|
|
}
|
|
|
|
|
2021-05-08 16:36:00 +00:00
|
|
|
func (s *Server) serveUDP(insertHandler func(r io.Reader) error) {
|
2020-12-08 18:49:32 +00:00
|
|
|
gomaxprocs := cgroup.AvailableCPUs()
|
2019-05-22 21:16:55 +00:00
|
|
|
var wg sync.WaitGroup
|
|
|
|
for i := 0; i < gomaxprocs; i++ {
|
|
|
|
wg.Add(1)
|
|
|
|
go func() {
|
|
|
|
defer wg.Done()
|
|
|
|
var bb bytesutil.ByteBuffer
|
2022-01-31 22:18:39 +00:00
|
|
|
bb.B = bytesutil.ResizeNoCopyNoOverallocate(bb.B, 64*1024)
|
2019-05-22 21:16:55 +00:00
|
|
|
for {
|
|
|
|
bb.Reset()
|
|
|
|
bb.B = bb.B[:cap(bb.B)]
|
2021-05-08 16:36:00 +00:00
|
|
|
n, addr, err := s.lnUDP.ReadFrom(bb.B)
|
2019-05-22 21:16:55 +00:00
|
|
|
if err != nil {
|
|
|
|
writeErrorsUDP.Inc()
|
2020-06-30 21:15:34 +00:00
|
|
|
var ne net.Error
|
|
|
|
if errors.As(err, &ne) {
|
2019-05-22 21:16:55 +00:00
|
|
|
if ne.Temporary() {
|
2021-05-08 16:36:00 +00:00
|
|
|
logger.Errorf("graphite: temporary error when listening for UDP addr %q: %s", s.lnUDP.LocalAddr(), err)
|
2019-05-22 21:16:55 +00:00
|
|
|
time.Sleep(time.Second)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if strings.Contains(err.Error(), "use of closed network connection") {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
logger.Errorf("cannot read Graphite UDP data: %s", err)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
bb.B = bb.B[:n]
|
|
|
|
writeRequestsUDP.Inc()
|
|
|
|
if err := insertHandler(bb.NewReader()); err != nil {
|
|
|
|
writeErrorsUDP.Inc()
|
2021-05-08 16:36:00 +00:00
|
|
|
logger.Errorf("error in UDP Graphite conn %q<->%q: %s", s.lnUDP.LocalAddr(), addr, err)
|
2019-05-22 21:16:55 +00:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
}
|
|
|
|
wg.Wait()
|
|
|
|
}
|