core/service/service.go
2022-03-16 21:21:47 +08:00

121 lines
2.3 KiB
Go

package service
import (
"context"
"net"
"time"
"github.com/go-gost/core/admission"
"github.com/go-gost/core/handler"
"github.com/go-gost/core/listener"
"github.com/go-gost/core/logger"
"github.com/go-gost/core/metrics"
)
type options struct {
admission admission.Admission
logger logger.Logger
}
type Option func(opts *options)
func AdmissionOption(admission admission.Admission) Option {
return func(opts *options) {
opts.admission = admission
}
}
func LoggerOption(logger logger.Logger) Option {
return func(opts *options) {
opts.logger = logger
}
}
type Service interface {
Serve() error
Addr() net.Addr
Close() error
}
type service struct {
name string
listener listener.Listener
handler handler.Handler
options options
}
func NewService(name string, ln listener.Listener, h handler.Handler, opts ...Option) Service {
var options options
for _, opt := range opts {
opt(&options)
}
return &service{
name: name,
listener: ln,
handler: h,
options: options,
}
}
func (s *service) Addr() net.Addr {
return s.listener.Addr()
}
func (s *service) Close() error {
return s.listener.Close()
}
func (s *service) Serve() error {
metrics.Services().Inc()
defer metrics.Services().Dec()
var tempDelay time.Duration
for {
conn, e := s.listener.Accept()
if e != nil {
if ne, ok := e.(net.Error); ok && ne.Temporary() {
if tempDelay == 0 {
tempDelay = 1 * time.Second
} else {
tempDelay *= 2
}
if max := 5 * time.Second; tempDelay > max {
tempDelay = max
}
s.options.logger.Warnf("accept: %v, retrying in %v", e, tempDelay)
time.Sleep(tempDelay)
continue
}
s.options.logger.Errorf("accept: %v", e)
return e
}
tempDelay = 0
if s.options.admission != nil &&
!s.options.admission.Admit(conn.RemoteAddr().String()) {
conn.Close()
continue
}
go func() {
metrics.Requests(s.name).Inc()
metrics.RequestsInFlight(s.name).Inc()
defer metrics.RequestsInFlight(s.name).Dec()
start := time.Now()
defer func() {
metrics.RequestSeconds(s.name).Observe(time.Since(start).Seconds())
}()
if err := s.handler.Handle(
context.Background(),
conn,
); err != nil {
s.options.logger.Error(err)
metrics.HandlerErrors(s.name).Inc()
}
}()
}
}