init commit

This commit is contained in:
flswld
2022-11-20 15:38:00 +08:00
parent eda2b643b9
commit 3efed3defe
5834 changed files with 636508 additions and 0 deletions

6
air/cmd/application.toml Normal file
View File

@@ -0,0 +1,6 @@
http_port = 8086
[logger]
level = "DEBUG"
method = "CONSOLE"
track_line = true

55
air/cmd/main.go Normal file
View File

@@ -0,0 +1,55 @@
package main
import (
"air/controller"
"air/service"
"flswld.com/common/config"
"flswld.com/logger"
"github.com/arl/statsviz"
"net/http"
_ "net/http/pprof"
"os"
"os/signal"
"syscall"
"time"
)
func main() {
filePath := "./application.toml"
config.InitConfig(filePath)
logger.InitLogger()
logger.LOG.Info("air start")
go func() {
// 性能检测
err := statsviz.RegisterDefault()
if err != nil {
logger.LOG.Error("statsviz init error: %v", err)
}
err = http.ListenAndServe("0.0.0.0:1234", nil)
if err != nil {
logger.LOG.Error("perf debug http start error: %v", err)
}
}()
svc := service.NewService()
_ = controller.NewController(svc)
c := make(chan os.Signal, 1)
signal.Notify(c, syscall.SIGHUP, syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGINT)
for {
s := <-c
logger.LOG.Info("get a signal %s", s.String())
switch s {
case syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGINT:
logger.LOG.Info("air exit")
time.Sleep(time.Second)
return
case syscall.SIGHUP:
default:
return
}
}
}

View File

@@ -0,0 +1,48 @@
package controller
import (
"air/service"
"flswld.com/common/config"
"github.com/gin-gonic/gin"
"strconv"
)
type Controller struct {
service *service.Service
}
func NewController(service *service.Service) (r *Controller) {
r = new(Controller)
r.service = service
go r.registerRouter()
return r
}
func (c *Controller) registerRouter() {
if config.CONF.Logger.Level == "DEBUG" {
gin.SetMode(gin.DebugMode)
} else {
gin.SetMode(gin.ReleaseMode)
}
engine := gin.Default()
// HTTP
engine.GET("/http/fetch", c.fetchHttpService)
engine.GET("/http/fetch/all", c.fetchAllHttpService)
engine.POST("/http/reg", c.registerHttpService)
engine.POST("/http/cancel", c.cancelHttpService)
engine.POST("/http/ka", c.httpKeepalive)
// RPC
engine.GET("/rpc/fetch", c.fetchRpcService)
engine.GET("/rpc/fetch/all", c.fetchAllRpcService)
engine.POST("/rpc/reg", c.registerRpcService)
engine.POST("/rpc/cancel", c.cancelRpcService)
engine.POST("/rpc/ka", c.rpcKeepalive)
// 长轮询
engine.GET("/poll/http", c.pollHttpService)
engine.GET("/poll/http/all", c.pollAllHttpService)
engine.GET("/poll/rpc", c.pollRpcService)
engine.GET("/poll/rpc/all", c.pollAllRpcService)
port := strconv.FormatInt(int64(config.CONF.HttpPort), 10)
portStr := ":" + port
_ = engine.Run(portStr)
}

View File

@@ -0,0 +1,39 @@
package controller
import "github.com/gin-gonic/gin"
// 获取HTTP服务
func (c *Controller) fetchHttpService(context *gin.Context) {
inst := c.service.FetchHttpService(context.Query("name"))
context.JSON(200, gin.H{
"code": 0,
"instance": inst,
})
}
// 获取所有HTTP服务
func (c *Controller) fetchAllHttpService(context *gin.Context) {
svc := c.service.FetchAllHttpService()
context.JSON(200, gin.H{
"code": 0,
"service": svc,
})
}
// 获取RPC服务
func (c *Controller) fetchRpcService(context *gin.Context) {
inst := c.service.FetchRpcService(context.Query("name"))
context.JSON(200, gin.H{
"code": 0,
"instance": inst,
})
}
// 获取所有RPC服务
func (c *Controller) fetchAllRpcService(context *gin.Context) {
svc := c.service.FetchAllRpcService()
context.JSON(200, gin.H{
"code": 0,
"service": svc,
})
}

View File

@@ -0,0 +1,35 @@
package controller
import (
"air/entity"
"flswld.com/logger"
"github.com/gin-gonic/gin"
)
// HTTP心跳
func (c *Controller) httpKeepalive(context *gin.Context) {
inst := new(entity.Instance)
err := context.ShouldBindJSON(inst)
if err != nil {
logger.LOG.Error("parse json error: %v", err)
return
}
c.service.HttpKeepalive(*inst)
context.JSON(200, gin.H{
"code": 0,
})
}
// RPC心跳
func (c *Controller) rpcKeepalive(context *gin.Context) {
inst := new(entity.Instance)
err := context.ShouldBindJSON(inst)
if err != nil {
logger.LOG.Error("parse json error: %v", err)
return
}
c.service.RpcKeepalive(*inst)
context.JSON(200, gin.H{
"code": 0,
})
}

View File

@@ -0,0 +1,85 @@
package controller
import (
"github.com/gin-gonic/gin"
"net/http"
"time"
)
func (c *Controller) pollHttpService(context *gin.Context) {
serviceName := context.Query("name")
recvrNtfr := c.service.RegistryHttpNotifyReceiver(serviceName)
timeout := time.NewTicker(time.Second * 30)
select {
case inst := <-recvrNtfr.NotifyChannel:
c.service.CancelHttpNotifyReceiver(serviceName, recvrNtfr.Id)
context.JSON(http.StatusOK, gin.H{
"code": 0,
"instance": inst,
})
case <-timeout.C:
c.service.CancelHttpNotifyReceiver(serviceName, recvrNtfr.Id)
context.JSON(http.StatusOK, gin.H{
"code": 0,
"instance": nil,
})
}
}
func (c *Controller) pollAllHttpService(context *gin.Context) {
recvrNtfr := c.service.RegistryAllHttpNotifyReceiver()
timeout := time.NewTicker(time.Second * 30)
select {
case svc := <-recvrNtfr.NotifyChannel:
c.service.CancelAllHttpNotifyReceiver(recvrNtfr.Id)
context.JSON(http.StatusOK, gin.H{
"code": 0,
"service": svc,
})
case <-timeout.C:
c.service.CancelAllHttpNotifyReceiver(recvrNtfr.Id)
context.JSON(http.StatusOK, gin.H{
"code": 0,
"service": nil,
})
}
}
func (c *Controller) pollRpcService(context *gin.Context) {
serviceName := context.Query("name")
recvrNtfr := c.service.RegistryRpcNotifyReceiver(serviceName)
timeout := time.NewTicker(time.Second * 30)
select {
case inst := <-recvrNtfr.NotifyChannel:
c.service.CancelRpcNotifyReceiver(serviceName, recvrNtfr.Id)
context.JSON(http.StatusOK, gin.H{
"code": 0,
"instance": inst,
})
case <-timeout.C:
c.service.CancelRpcNotifyReceiver(serviceName, recvrNtfr.Id)
context.JSON(http.StatusOK, gin.H{
"code": 0,
"instance": nil,
})
}
}
func (c *Controller) pollAllRpcService(context *gin.Context) {
recvrNtfr := c.service.RegistryAllRpcNotifyReceiver()
timeout := time.NewTicker(time.Second * 30)
select {
case svc := <-recvrNtfr.NotifyChannel:
c.service.CancelAllRpcNotifyReceiver(recvrNtfr.Id)
context.JSON(http.StatusOK, gin.H{
"code": 0,
"service": svc,
})
case <-timeout.C:
c.service.CancelAllRpcNotifyReceiver(recvrNtfr.Id)
context.JSON(http.StatusOK, gin.H{
"code": 0,
"service": nil,
})
}
}

View File

@@ -0,0 +1,63 @@
package controller
import (
"air/entity"
"flswld.com/logger"
"github.com/gin-gonic/gin"
)
// 注册HTTP服务
func (c *Controller) registerHttpService(context *gin.Context) {
inst := new(entity.Instance)
err := context.ShouldBindJSON(inst)
if err != nil {
logger.LOG.Error("parse json error: %v", err)
return
}
c.service.RegisterHttpService(*inst)
context.JSON(200, gin.H{
"code": 0,
})
}
// 取消注册HTTP服务
func (c *Controller) cancelHttpService(context *gin.Context) {
inst := new(entity.Instance)
err := context.ShouldBindJSON(inst)
if err != nil {
logger.LOG.Error("parse json error: %v", err)
return
}
c.service.CancelHttpService(*inst)
context.JSON(200, gin.H{
"code": 0,
})
}
// 注册RPC服务
func (c *Controller) registerRpcService(context *gin.Context) {
inst := new(entity.Instance)
err := context.ShouldBindJSON(inst)
if err != nil {
logger.LOG.Error("parse json error: %v", err)
return
}
c.service.RegisterRpcService(*inst)
context.JSON(200, gin.H{
"code": 0,
})
}
// 取消注册RPC服务
func (c *Controller) cancelRpcService(context *gin.Context) {
inst := new(entity.Instance)
err := context.ShouldBindJSON(inst)
if err != nil {
logger.LOG.Error("parse json error: %v", err)
return
}
c.service.CancelRpcService(*inst)
context.JSON(200, gin.H{
"code": 0,
})
}

8
air/entity/instance.go Normal file
View File

@@ -0,0 +1,8 @@
package entity
// 服务实例实体类
type Instance struct {
ServiceName string `json:"service_name"`
InstanceName string `json:"instance_name"`
InstanceAddr string `json:"instance_addr"`
}

33
air/go.mod Normal file
View File

@@ -0,0 +1,33 @@
module air
go 1.19
require flswld.com/common v0.0.0-incompatible
replace flswld.com/common => ../common
require flswld.com/logger v0.0.0-incompatible
replace flswld.com/logger => ../logger
require github.com/gin-gonic/gin v1.6.3
require github.com/arl/statsviz v0.5.1
require (
github.com/BurntSushi/toml v0.3.1 // indirect
github.com/gin-contrib/sse v0.1.0 // indirect
github.com/go-playground/locales v0.13.0 // indirect
github.com/go-playground/universal-translator v0.17.0 // indirect
github.com/go-playground/validator/v10 v10.2.0 // indirect
github.com/golang/protobuf v1.3.3 // indirect
github.com/gorilla/websocket v1.4.2 // indirect
github.com/json-iterator/go v1.1.9 // indirect
github.com/leodido/go-urn v1.2.0 // indirect
github.com/mattn/go-isatty v0.0.12 // indirect
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 // indirect
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 // indirect
github.com/ugorji/go/codec v1.1.7 // indirect
golang.org/x/sys v0.0.0-20200116001909-b77594299b42 // indirect
gopkg.in/yaml.v2 v2.2.8 // indirect
)

52
air/go.sum Normal file
View File

@@ -0,0 +1,52 @@
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/arl/statsviz v0.5.1 h1:3HY0ZEB738JtguWsD1Tf1pFJZiCcWUmYRq/3OTYKaSI=
github.com/arl/statsviz v0.5.1/go.mod h1:zDnjgRblGm1Dyd7J5YlbH7gM1/+HRC+SfkhZhQb5AnM=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
github.com/gin-gonic/gin v1.6.3 h1:ahKqKTFpO5KTPHxWZjEdPScmYaGtLo8Y4DMHoEsnp14=
github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M=
github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A=
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q=
github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no=
github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
github.com/go-playground/validator/v10 v10.2.0 h1:KgJ0snyC2R9VXYN2rneOtQcw5aHQB1Vv0sFl1UcHBOY=
github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI=
github.com/golang/protobuf v1.3.3 h1:gyjaxf+svBWX08ZjK86iN9geUJF0H6gp2IRKX6Nf6/I=
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/json-iterator/go v1.1.9 h1:9yzud/Ht36ygwatGx56VwCZtlI/2AD15T1X2sjSuGns=
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y=
github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 h1:Esafd1046DLDQ0W1YjYsBW+p8U2u7vzgW2SQVmlNazg=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs=
github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42 h1:vEOn+mP2zCOVzKckCZy6YsCtDblrpj/w7B9nxGNELpg=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=

View File

@@ -0,0 +1,89 @@
package service
import "air/entity"
// 获取HTTP服务
func (s *Service) FetchHttpService(name string) (r []entity.Instance) {
s.httpServiceMapLock.RLock()
instanceMap := s.httpServiceMap[name]
s.httpServiceMapLock.RUnlock()
r = make([]entity.Instance, 0)
if instanceMap == nil {
return r
}
instanceMap.lock.RLock()
for k, v := range instanceMap.Imap {
instance := new(entity.Instance)
instance.ServiceName = name
instance.InstanceName = k
instance.InstanceAddr = v.Address
r = append(r, *instance)
}
instanceMap.lock.RUnlock()
return r
}
// 获取所有HTTP服务
func (s *Service) FetchAllHttpService() (r map[string][]entity.Instance) {
s.httpServiceMapLock.RLock()
serviceMap := s.httpServiceMap
s.httpServiceMapLock.RUnlock()
r = make(map[string][]entity.Instance)
for k, v := range serviceMap {
instanceSlice := make([]entity.Instance, 0)
v.lock.RLock()
for kk, vv := range v.Imap {
instance := new(entity.Instance)
instance.ServiceName = k
instance.InstanceName = kk
instance.InstanceAddr = vv.Address
instanceSlice = append(instanceSlice, *instance)
}
v.lock.RUnlock()
r[k] = instanceSlice
}
return r
}
// 获取RPC服务
func (s *Service) FetchRpcService(name string) (r []entity.Instance) {
s.rpcServiceMapLock.RLock()
instanceMap := s.rpcServiceMap[name]
s.rpcServiceMapLock.RUnlock()
r = make([]entity.Instance, 0)
if instanceMap == nil {
return r
}
instanceMap.lock.RLock()
for k, v := range instanceMap.Imap {
instance := new(entity.Instance)
instance.ServiceName = name
instance.InstanceName = k
instance.InstanceAddr = v.Address
r = append(r, *instance)
}
instanceMap.lock.RUnlock()
return r
}
// 获取所有RPC服务
func (s *Service) FetchAllRpcService() (r map[string][]entity.Instance) {
s.rpcServiceMapLock.RLock()
serviceMap := s.rpcServiceMap
s.rpcServiceMapLock.RUnlock()
r = make(map[string][]entity.Instance)
for k, v := range serviceMap {
instanceSlice := make([]entity.Instance, 0)
v.lock.RLock()
for kk, vv := range v.Imap {
instance := new(entity.Instance)
instance.ServiceName = k
instance.InstanceName = kk
instance.InstanceAddr = vv.Address
instanceSlice = append(instanceSlice, *instance)
}
v.lock.RUnlock()
r[k] = instanceSlice
}
return r
}

View File

@@ -0,0 +1,136 @@
package service
import (
"air/entity"
"flswld.com/logger"
"time"
)
// HTTP心跳
func (s *Service) HttpKeepalive(instance entity.Instance) {
nowTime := time.Now().Unix()
s.httpServiceMapLock.RLock()
instanceMap := s.httpServiceMap[instance.ServiceName]
s.httpServiceMapLock.RUnlock()
if instanceMap != nil {
instanceMap.lock.Lock()
instanceData := instanceMap.Imap[instance.InstanceName]
if instanceData != nil {
instanceData.LastAliveTime = nowTime
} else {
logger.LOG.Error("recv not exist instance http keepalive, instance name: %v", instance.InstanceName)
}
instanceMap.lock.Unlock()
} else {
logger.LOG.Error("recv not exist service http keepalive, service name: %v", instance.ServiceName)
}
}
// RPC心跳
func (s *Service) RpcKeepalive(instance entity.Instance) {
nowTime := time.Now().Unix()
s.rpcServiceMapLock.RLock()
instanceMap := s.rpcServiceMap[instance.ServiceName]
s.rpcServiceMapLock.RUnlock()
if instanceMap != nil {
instanceMap.lock.Lock()
instanceData := instanceMap.Imap[instance.InstanceName]
if instanceData != nil {
instanceData.LastAliveTime = nowTime
} else {
logger.LOG.Error("recv not exist instance rpc keepalive, instance name: %v", instance.InstanceName)
}
instanceMap.lock.Unlock()
} else {
logger.LOG.Error("recv not exist service rpc keepalive, service name: %v", instance.ServiceName)
}
}
// 定时移除掉线服务
func (s *Service) removeDeadService() {
ticker := time.NewTicker(time.Second * 60)
for {
<-ticker.C
nowTime := time.Now().Unix()
httpSvcChgFlagMap := make(map[string]bool)
httpSvcChgMap := make(map[string]*InstanceMap)
httpSvcDelMap := make(map[string]*InstanceMap)
s.httpServiceMapLock.RLock()
for svcName, svcInstMap := range s.httpServiceMap {
svcInstMap.lock.Lock()
for instName, instData := range svcInstMap.Imap {
if nowTime-instData.LastAliveTime > 60 {
httpSvcChgFlagMap[svcName] = true
if httpSvcDelMap[svcName] == nil {
httpSvcDelMap[svcName] = new(InstanceMap)
httpSvcDelMap[svcName].Imap = make(map[string]*InstanceData)
}
httpSvcDelMap[svcName].Imap[instName] = instData
delete(svcInstMap.Imap, instName)
} else {
if httpSvcChgMap[svcName] == nil {
httpSvcChgMap[svcName] = new(InstanceMap)
httpSvcChgMap[svcName].Imap = make(map[string]*InstanceData)
}
httpSvcChgMap[svcName].Imap[instName] = instData
}
}
svcInstMap.lock.Unlock()
}
s.httpServiceMapLock.RUnlock()
for svcName, instMap := range httpSvcDelMap {
for instName, instData := range instMap.Imap {
logger.LOG.Info("remove timeout http service, service name: %v, instance name: %v, instance data: %v", svcName, instName, instData)
}
}
for svcName, _ := range httpSvcChgMap {
if !httpSvcChgFlagMap[svcName] {
delete(httpSvcChgMap, svcName)
}
}
if len(httpSvcChgMap) != 0 {
s.httpSvcChgNtfCh <- httpSvcChgMap
}
rpcSvcChgFlagMap := make(map[string]bool)
rpcSvcChgMap := make(map[string]*InstanceMap)
rpcSvcDelMap := make(map[string]*InstanceMap)
s.rpcServiceMapLock.RLock()
for svcName, svcInstMap := range s.rpcServiceMap {
svcInstMap.lock.Lock()
for instName, instData := range svcInstMap.Imap {
if nowTime-instData.LastAliveTime > 60 {
rpcSvcChgFlagMap[svcName] = true
if rpcSvcDelMap[svcName] == nil {
rpcSvcDelMap[svcName] = new(InstanceMap)
rpcSvcDelMap[svcName].Imap = make(map[string]*InstanceData)
}
rpcSvcDelMap[svcName].Imap[instName] = instData
delete(svcInstMap.Imap, instName)
} else {
if rpcSvcChgMap[svcName] == nil {
rpcSvcChgMap[svcName] = new(InstanceMap)
rpcSvcChgMap[svcName].Imap = make(map[string]*InstanceData)
}
rpcSvcChgMap[svcName].Imap[instName] = instData
}
}
svcInstMap.lock.Unlock()
}
s.rpcServiceMapLock.RUnlock()
for svcName, instMap := range rpcSvcDelMap {
for instName, instData := range instMap.Imap {
logger.LOG.Info("remove timeout rpc service, service name: %v, instance name: %v, instance data: %v", svcName, instName, instData)
}
}
for svcName, _ := range rpcSvcChgMap {
if !rpcSvcChgFlagMap[svcName] {
delete(rpcSvcChgMap, svcName)
}
}
if len(rpcSvcChgMap) != 0 {
s.rpcSvcChgNtfCh <- rpcSvcChgMap
}
}
}

190
air/service/poll_service.go Normal file
View File

@@ -0,0 +1,190 @@
package service
import (
"air/entity"
"flswld.com/logger"
"sync/atomic"
"time"
)
func (s *Service) watchServiceChange() {
s.watcher.receiverNotifierIdCounter = 0
s.watcher.httpRecvrNtfrMap = make(map[string]map[uint64]*ReceiverNotifier)
s.watcher.httpAllSvcRecvrNtfrMap = make(map[uint64]*AllServiceReceiverNotifier)
s.watcher.rpcRecvrNtfrMap = make(map[string]map[uint64]*ReceiverNotifier)
s.watcher.rpcAllSvcRecvrNtfrMap = make(map[uint64]*AllServiceReceiverNotifier)
go func() {
for {
imap := <-s.httpSvcChgNtfCh
for svcName, instMap := range imap {
// 给某个服务的接收者通知器发送消息
s.watcher.httpRecvrNtfrMapLock.RLock()
recvrNtfrMap := s.watcher.httpRecvrNtfrMap[svcName]
instList := make([]entity.Instance, 0)
for instName, instData := range instMap.Imap {
inst := new(entity.Instance)
inst.ServiceName = svcName
inst.InstanceName = instName
inst.InstanceAddr = instData.Address
instList = append(instList, *inst)
}
if recvrNtfrMap == nil || len(recvrNtfrMap) == 0 {
s.watcher.httpRecvrNtfrMapLock.RUnlock()
continue
}
for _, recvrNtfr := range recvrNtfrMap {
if time.Now().UnixNano()-recvrNtfr.CreateTime < int64(time.Second*30) {
logger.LOG.Debug("send http service change notify to receiver: %d", recvrNtfr.Id)
recvrNtfr.NotifyChannel <- instList
}
close(recvrNtfr.NotifyChannel)
}
s.watcher.httpRecvrNtfrMapLock.RUnlock()
}
// 给全体服务的接收者通知器发送消息
s.watcher.httpAllSvcRecvrNtfrMapLock.RLock()
if len(s.watcher.httpAllSvcRecvrNtfrMap) == 0 {
s.watcher.httpAllSvcRecvrNtfrMapLock.RUnlock()
continue
}
svcMap := s.FetchAllHttpService()
for _, recvrNtfr := range s.watcher.httpAllSvcRecvrNtfrMap {
if time.Now().UnixNano()-recvrNtfr.CreateTime < int64(time.Second*30) {
logger.LOG.Debug("send all http service change notify to receiver: %d", recvrNtfr.Id)
recvrNtfr.NotifyChannel <- svcMap
}
close(recvrNtfr.NotifyChannel)
}
s.watcher.httpAllSvcRecvrNtfrMapLock.RUnlock()
}
}()
go func() {
for {
imap := <-s.rpcSvcChgNtfCh
for svcName, instMap := range imap {
// 给某个服务的接收者通知器发送消息
s.watcher.rpcRecvrNtfrMapLock.RLock()
recvrNtfrMap := s.watcher.rpcRecvrNtfrMap[svcName]
instList := make([]entity.Instance, 0)
for instName, instData := range instMap.Imap {
inst := new(entity.Instance)
inst.ServiceName = svcName
inst.InstanceName = instName
inst.InstanceAddr = instData.Address
instList = append(instList, *inst)
}
if recvrNtfrMap == nil || len(recvrNtfrMap) == 0 {
s.watcher.rpcRecvrNtfrMapLock.RUnlock()
continue
}
for _, recvrNtfr := range recvrNtfrMap {
if time.Now().UnixNano()-recvrNtfr.CreateTime < int64(time.Second*30) {
logger.LOG.Debug("send rpc service change notify to receiver: %d", recvrNtfr.Id)
recvrNtfr.NotifyChannel <- instList
}
close(recvrNtfr.NotifyChannel)
}
s.watcher.rpcRecvrNtfrMapLock.RUnlock()
}
// 给全体服务的接收者通知器发送消息
s.watcher.rpcAllSvcRecvrNtfrMapLock.RLock()
if len(s.watcher.rpcAllSvcRecvrNtfrMap) == 0 {
s.watcher.rpcAllSvcRecvrNtfrMapLock.RUnlock()
continue
}
svcMap := s.FetchAllRpcService()
for _, recvrNtfr := range s.watcher.rpcAllSvcRecvrNtfrMap {
if time.Now().UnixNano()-recvrNtfr.CreateTime < int64(time.Second*30) {
logger.LOG.Debug("send all rpc service change notify to receiver: %d", recvrNtfr.Id)
recvrNtfr.NotifyChannel <- svcMap
}
close(recvrNtfr.NotifyChannel)
}
s.watcher.rpcAllSvcRecvrNtfrMapLock.RUnlock()
}
}()
}
// 注册HTTP服务变化通知接收者
func (s *Service) RegistryHttpNotifyReceiver(serviceName string) *ReceiverNotifier {
recvrNtfr := new(ReceiverNotifier)
recvrNtfr.Id = atomic.AddUint64(&s.watcher.receiverNotifierIdCounter, 1)
recvrNtfr.CreateTime = time.Now().UnixNano()
recvrNtfr.NotifyChannel = make(chan []entity.Instance, 0)
s.watcher.httpRecvrNtfrMapLock.Lock()
if s.watcher.httpRecvrNtfrMap[serviceName] == nil {
s.watcher.httpRecvrNtfrMap[serviceName] = make(map[uint64]*ReceiverNotifier)
}
s.watcher.httpRecvrNtfrMap[serviceName][recvrNtfr.Id] = recvrNtfr
s.watcher.httpRecvrNtfrMapLock.Unlock()
return recvrNtfr
}
// 取消HTTP服务变化通知接收者
func (s *Service) CancelHttpNotifyReceiver(serviceName string, id uint64) {
s.watcher.httpRecvrNtfrMapLock.Lock()
delete(s.watcher.httpRecvrNtfrMap[serviceName], id)
s.watcher.httpRecvrNtfrMapLock.Unlock()
}
// 注册全体HTTP服务变化通知接收者
func (s *Service) RegistryAllHttpNotifyReceiver() *AllServiceReceiverNotifier {
recvrNtfr := new(AllServiceReceiverNotifier)
recvrNtfr.Id = atomic.AddUint64(&s.watcher.receiverNotifierIdCounter, 1)
recvrNtfr.CreateTime = time.Now().UnixNano()
recvrNtfr.NotifyChannel = make(chan map[string][]entity.Instance, 0)
s.watcher.httpAllSvcRecvrNtfrMapLock.Lock()
s.watcher.httpAllSvcRecvrNtfrMap[recvrNtfr.Id] = recvrNtfr
s.watcher.httpAllSvcRecvrNtfrMapLock.Unlock()
return recvrNtfr
}
// 取消全体HTTP服务变化通知接收者
func (s *Service) CancelAllHttpNotifyReceiver(id uint64) {
s.watcher.httpAllSvcRecvrNtfrMapLock.Lock()
delete(s.watcher.httpAllSvcRecvrNtfrMap, id)
s.watcher.httpAllSvcRecvrNtfrMapLock.Unlock()
}
// 注册RPC服务变化通知接收者
func (s *Service) RegistryRpcNotifyReceiver(serviceName string) *ReceiverNotifier {
recvrNtfr := new(ReceiverNotifier)
recvrNtfr.Id = atomic.AddUint64(&s.watcher.receiverNotifierIdCounter, 1)
recvrNtfr.CreateTime = time.Now().UnixNano()
recvrNtfr.NotifyChannel = make(chan []entity.Instance, 0)
s.watcher.rpcRecvrNtfrMapLock.Lock()
if s.watcher.rpcRecvrNtfrMap[serviceName] == nil {
s.watcher.rpcRecvrNtfrMap[serviceName] = make(map[uint64]*ReceiverNotifier)
}
s.watcher.rpcRecvrNtfrMap[serviceName][recvrNtfr.Id] = recvrNtfr
s.watcher.rpcRecvrNtfrMapLock.Unlock()
return recvrNtfr
}
// 取消RPC服务变化通知接收者
func (s *Service) CancelRpcNotifyReceiver(serviceName string, id uint64) {
s.watcher.rpcRecvrNtfrMapLock.Lock()
delete(s.watcher.rpcRecvrNtfrMap[serviceName], id)
s.watcher.rpcRecvrNtfrMapLock.Unlock()
}
// 注册全体RPC服务变化通知接收者
func (s *Service) RegistryAllRpcNotifyReceiver() *AllServiceReceiverNotifier {
recvrNtfr := new(AllServiceReceiverNotifier)
recvrNtfr.Id = atomic.AddUint64(&s.watcher.receiverNotifierIdCounter, 1)
recvrNtfr.CreateTime = time.Now().UnixNano()
recvrNtfr.NotifyChannel = make(chan map[string][]entity.Instance, 0)
s.watcher.rpcAllSvcRecvrNtfrMapLock.Lock()
s.watcher.rpcAllSvcRecvrNtfrMap[recvrNtfr.Id] = recvrNtfr
s.watcher.rpcAllSvcRecvrNtfrMapLock.Unlock()
return recvrNtfr
}
// 取消全体RPC服务变化通知接收者
func (s *Service) CancelAllRpcNotifyReceiver(id uint64) {
s.watcher.rpcAllSvcRecvrNtfrMapLock.Lock()
delete(s.watcher.rpcAllSvcRecvrNtfrMap, id)
s.watcher.rpcAllSvcRecvrNtfrMapLock.Unlock()
}

View File

@@ -0,0 +1,103 @@
package service
import (
"air/entity"
"flswld.com/common/utils/object"
"time"
)
// 注册HTTP服务
func (s *Service) RegisterHttpService(instance entity.Instance) bool {
nowTime := time.Now().Unix()
s.httpServiceMapLock.Lock()
instanceMap := s.httpServiceMap[instance.ServiceName]
if instanceMap == nil {
instanceMap = new(InstanceMap)
instanceMap.Imap = make(map[string]*InstanceData)
}
instanceMap.lock.Lock()
instanceData := instanceMap.Imap[instance.InstanceName]
if instanceData == nil {
instanceData = new(InstanceData)
}
instanceData.Address = instance.InstanceAddr
instanceData.LastAliveTime = nowTime
instanceMap.Imap[instance.InstanceName] = instanceData
s.httpServiceMap[instance.ServiceName] = instanceMap
instanceMap.lock.Unlock()
s.httpServiceMapLock.Unlock()
changeInst := make(map[string]*InstanceMap)
instanceMapCopy := new(InstanceMap)
instanceMap.lock.RLock()
_ = object.ObjectDeepCopy(instanceMap, instanceMapCopy)
instanceMap.lock.RUnlock()
changeInst[instance.ServiceName] = instanceMapCopy
s.httpSvcChgNtfCh <- changeInst
return true
}
// 取消注册HTTP服务
func (s *Service) CancelHttpService(instance entity.Instance) bool {
s.httpServiceMapLock.RLock()
instanceMap := s.httpServiceMap[instance.ServiceName]
s.httpServiceMapLock.RUnlock()
instanceMap.lock.Lock()
delete(instanceMap.Imap, instance.InstanceName)
instanceMap.lock.Unlock()
changeInst := make(map[string]*InstanceMap)
instanceMapCopy := new(InstanceMap)
instanceMap.lock.RLock()
_ = object.ObjectDeepCopy(instanceMap, instanceMapCopy)
instanceMap.lock.RUnlock()
changeInst[instance.ServiceName] = instanceMapCopy
s.httpSvcChgNtfCh <- changeInst
return true
}
// 注册RPC服务
func (s *Service) RegisterRpcService(instance entity.Instance) bool {
nowTime := time.Now().Unix()
s.rpcServiceMapLock.Lock()
instanceMap := s.rpcServiceMap[instance.ServiceName]
if instanceMap == nil {
instanceMap = new(InstanceMap)
instanceMap.Imap = make(map[string]*InstanceData)
}
instanceMap.lock.Lock()
instanceData := instanceMap.Imap[instance.InstanceName]
if instanceData == nil {
instanceData = new(InstanceData)
}
instanceData.Address = instance.InstanceAddr
instanceData.LastAliveTime = nowTime
instanceMap.Imap[instance.InstanceName] = instanceData
s.rpcServiceMap[instance.ServiceName] = instanceMap
instanceMap.lock.Unlock()
s.rpcServiceMapLock.Unlock()
changeInst := make(map[string]*InstanceMap)
instanceMapCopy := new(InstanceMap)
instanceMap.lock.RLock()
_ = object.ObjectDeepCopy(instanceMap, instanceMapCopy)
instanceMap.lock.RUnlock()
changeInst[instance.ServiceName] = instanceMapCopy
s.rpcSvcChgNtfCh <- changeInst
return true
}
// 取消注册RPC服务
func (s *Service) CancelRpcService(instance entity.Instance) bool {
s.rpcServiceMapLock.RLock()
instanceMap := s.rpcServiceMap[instance.ServiceName]
s.rpcServiceMapLock.RUnlock()
instanceMap.lock.Lock()
delete(instanceMap.Imap, instance.InstanceName)
instanceMap.lock.Unlock()
changeInst := make(map[string]*InstanceMap)
instanceMapCopy := new(InstanceMap)
instanceMap.lock.RLock()
_ = object.ObjectDeepCopy(instanceMap, instanceMapCopy)
instanceMap.lock.RUnlock()
changeInst[instance.ServiceName] = instanceMapCopy
s.rpcSvcChgNtfCh <- changeInst
return true
}

75
air/service/service.go Normal file
View File

@@ -0,0 +1,75 @@
package service
import (
"air/entity"
"sync"
)
// 实例数据
type InstanceData struct {
// 实例地址
Address string
// 最后心跳时间
LastAliveTime int64
}
// 服务实例集合
type InstanceMap struct {
// key:实例名 value:实例数据
Imap map[string]*InstanceData
lock sync.RWMutex
}
type ReceiverNotifier struct {
Id uint64
CreateTime int64
NotifyChannel chan []entity.Instance
}
type AllServiceReceiverNotifier struct {
Id uint64
CreateTime int64
NotifyChannel chan map[string][]entity.Instance
}
type Watcher struct {
receiverNotifierIdCounter uint64
// key1:服务名 key2:接收者通知器id value:接收者通知器
httpRecvrNtfrMap map[string]map[uint64]*ReceiverNotifier
httpRecvrNtfrMapLock sync.RWMutex
// key:接收者通知器id value:接收者通知器
httpAllSvcRecvrNtfrMap map[uint64]*AllServiceReceiverNotifier
httpAllSvcRecvrNtfrMapLock sync.RWMutex
// key1:服务名 key2:接收者通知器id value:接收者通知器
rpcRecvrNtfrMap map[string]map[uint64]*ReceiverNotifier
rpcRecvrNtfrMapLock sync.RWMutex
// key:接收者通知器id value:接收者通知器
rpcAllSvcRecvrNtfrMap map[uint64]*AllServiceReceiverNotifier
rpcAllSvcRecvrNtfrMapLock sync.RWMutex
}
// 注册服务
type Service struct {
// key:服务名 value:服务实例集合
httpServiceMap map[string]*InstanceMap
httpServiceMapLock sync.RWMutex
httpSvcChgNtfCh chan map[string]*InstanceMap
// key:服务名 value:服务实例集合
rpcServiceMap map[string]*InstanceMap
rpcServiceMapLock sync.RWMutex
rpcSvcChgNtfCh chan map[string]*InstanceMap
watcher *Watcher
}
// 构造函数
func NewService() (r *Service) {
r = new(Service)
r.httpServiceMap = make(map[string]*InstanceMap)
r.rpcServiceMap = make(map[string]*InstanceMap)
r.httpSvcChgNtfCh = make(chan map[string]*InstanceMap, 0)
r.rpcSvcChgNtfCh = make(chan map[string]*InstanceMap, 0)
r.watcher = new(Watcher)
go r.removeDeadService()
go r.watchServiceChange()
return r
}