unified gateway interface
This commit is contained in:
157
api/pkg/discovery/announcer.go
Normal file
157
api/pkg/discovery/announcer.go
Normal file
@@ -0,0 +1,157 @@
|
||||
package discovery
|
||||
|
||||
import (
|
||||
"os"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
msg "github.com/tech/sendico/pkg/messaging"
|
||||
"github.com/tech/sendico/pkg/mlogger"
|
||||
)
|
||||
|
||||
type Announcer struct {
|
||||
logger mlogger.Logger
|
||||
producer msg.Producer
|
||||
sender string
|
||||
announce Announcement
|
||||
|
||||
startOnce sync.Once
|
||||
stopOnce sync.Once
|
||||
stopCh chan struct{}
|
||||
doneCh chan struct{}
|
||||
}
|
||||
|
||||
func NewAnnouncer(logger mlogger.Logger, producer msg.Producer, sender string, announce Announcement) *Announcer {
|
||||
if logger != nil {
|
||||
logger = logger.Named("discovery")
|
||||
}
|
||||
announce = normalizeAnnouncement(announce)
|
||||
if announce.Service == "" {
|
||||
announce.Service = strings.TrimSpace(sender)
|
||||
}
|
||||
if announce.ID == "" {
|
||||
announce.ID = DefaultInstanceID(announce.Service)
|
||||
}
|
||||
if announce.InvokeURI == "" && announce.Service != "" {
|
||||
announce.InvokeURI = DefaultInvokeURI(announce.Service)
|
||||
}
|
||||
return &Announcer{
|
||||
logger: logger,
|
||||
producer: producer,
|
||||
sender: strings.TrimSpace(sender),
|
||||
announce: announce,
|
||||
stopCh: make(chan struct{}),
|
||||
doneCh: make(chan struct{}),
|
||||
}
|
||||
}
|
||||
|
||||
func (a *Announcer) Start() {
|
||||
if a == nil {
|
||||
return
|
||||
}
|
||||
a.startOnce.Do(func() {
|
||||
if a.producer == nil {
|
||||
a.logWarn("Discovery announce skipped: producer not configured")
|
||||
close(a.doneCh)
|
||||
return
|
||||
}
|
||||
if strings.TrimSpace(a.announce.ID) == "" {
|
||||
a.logWarn("Discovery announce skipped: missing instance id")
|
||||
close(a.doneCh)
|
||||
return
|
||||
}
|
||||
a.sendAnnouncement()
|
||||
a.sendHeartbeat()
|
||||
go a.heartbeatLoop()
|
||||
})
|
||||
}
|
||||
|
||||
func (a *Announcer) Stop() {
|
||||
if a == nil {
|
||||
return
|
||||
}
|
||||
a.stopOnce.Do(func() {
|
||||
close(a.stopCh)
|
||||
<-a.doneCh
|
||||
})
|
||||
}
|
||||
|
||||
func (a *Announcer) heartbeatLoop() {
|
||||
defer close(a.doneCh)
|
||||
interval := time.Duration(a.announce.Health.IntervalSec) * time.Second
|
||||
if interval <= 0 {
|
||||
interval = time.Duration(DefaultHealthIntervalSec) * time.Second
|
||||
}
|
||||
ticker := time.NewTicker(interval)
|
||||
defer ticker.Stop()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-a.stopCh:
|
||||
return
|
||||
case <-ticker.C:
|
||||
a.sendHeartbeat()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (a *Announcer) sendAnnouncement() {
|
||||
env := NewServiceAnnounceEnvelope(a.sender, a.announce)
|
||||
if a.announce.Rail != "" {
|
||||
env = NewGatewayAnnounceEnvelope(a.sender, a.announce)
|
||||
}
|
||||
if err := a.producer.SendMessage(env); err != nil {
|
||||
a.logWarn("Failed to publish discovery announce: " + err.Error())
|
||||
return
|
||||
}
|
||||
a.logInfo("Discovery announce published")
|
||||
}
|
||||
|
||||
func (a *Announcer) sendHeartbeat() {
|
||||
hb := Heartbeat{
|
||||
ID: a.announce.ID,
|
||||
Status: "ok",
|
||||
TS: time.Now().Unix(),
|
||||
}
|
||||
if err := a.producer.SendMessage(NewHeartbeatEnvelope(a.sender, hb)); err != nil {
|
||||
a.logWarn("Failed to publish discovery heartbeat: " + err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func (a *Announcer) logInfo(message string) {
|
||||
if a.logger == nil {
|
||||
return
|
||||
}
|
||||
a.logger.Info(message)
|
||||
}
|
||||
|
||||
func (a *Announcer) logWarn(message string) {
|
||||
if a.logger == nil {
|
||||
return
|
||||
}
|
||||
a.logger.Warn(message)
|
||||
}
|
||||
|
||||
func DefaultInstanceID(service string) string {
|
||||
clean := strings.ToLower(strings.TrimSpace(service))
|
||||
if clean == "" {
|
||||
clean = "service"
|
||||
}
|
||||
host, _ := os.Hostname()
|
||||
host = strings.ToLower(strings.TrimSpace(host))
|
||||
uid := uuid.NewString()
|
||||
if host == "" {
|
||||
return clean + "_" + uid
|
||||
}
|
||||
return clean + "_" + host + "_" + uid
|
||||
}
|
||||
|
||||
func DefaultInvokeURI(service string) string {
|
||||
clean := strings.ToLower(strings.TrimSpace(service))
|
||||
if clean == "" {
|
||||
return ""
|
||||
}
|
||||
return "grpc://" + clean
|
||||
}
|
||||
Reference in New Issue
Block a user