mirror of
https://github.com/FlourishingWorld/hk4e.git
synced 2026-02-11 20:12:25 +08:00
init commit
This commit is contained in:
81
wind/air/discovery.go
Normal file
81
wind/air/discovery.go
Normal file
@@ -0,0 +1,81 @@
|
||||
package air
|
||||
|
||||
import (
|
||||
airClient "flswld.com/air-api/client"
|
||||
"flswld.com/common/config"
|
||||
"flswld.com/logger"
|
||||
"strings"
|
||||
"time"
|
||||
"wind/entity"
|
||||
)
|
||||
|
||||
type Air struct {
|
||||
serviceAddressMap *entity.AddressMap
|
||||
}
|
||||
|
||||
func NewAir(addressMap *entity.AddressMap) (r *Air) {
|
||||
r = new(Air)
|
||||
r.serviceAddressMap = addressMap
|
||||
airClient.SetAirAddr(config.CONF.Air.Addr, config.CONF.Air.Port)
|
||||
go r.fetchHttpService()
|
||||
go r.pollHttpService()
|
||||
return r
|
||||
}
|
||||
|
||||
func (a *Air) syncServiceMap(responseData *airClient.ResponseData) {
|
||||
a.serviceAddressMap.Lock.Lock()
|
||||
for _, v := range config.CONF.Routes {
|
||||
instanceSlice := responseData.Service[v.ServiceName]
|
||||
serviceAddress := make([]string, 0)
|
||||
for _, vv := range instanceSlice {
|
||||
if strings.Contains(vv.InstanceAddr, "http://") {
|
||||
serviceAddress = append(serviceAddress, vv.InstanceAddr)
|
||||
}
|
||||
}
|
||||
a.serviceAddressMap.Map[v.ServiceName] = serviceAddress
|
||||
}
|
||||
a.serviceAddressMap.Lock.Unlock()
|
||||
}
|
||||
|
||||
// 从注册中心获取所有服务
|
||||
func (a *Air) fetchHttpService() {
|
||||
ticker := time.NewTicker(time.Second * 600)
|
||||
for {
|
||||
var responseData *airClient.ResponseData
|
||||
var err error
|
||||
responseData, err = airClient.FetchAllHttpService()
|
||||
if err != nil {
|
||||
logger.LOG.Error("fetch all http service error: %v", err)
|
||||
return
|
||||
}
|
||||
a.syncServiceMap(responseData)
|
||||
a.serviceAddressMap.Lock.RLock()
|
||||
logger.LOG.Debug("fetch tick finished, serviceAddressMap: %v", a.serviceAddressMap.Map)
|
||||
a.serviceAddressMap.Lock.RUnlock()
|
||||
<-ticker.C
|
||||
}
|
||||
}
|
||||
|
||||
// 从注册中心长轮询监听所有服务变化
|
||||
func (a *Air) pollHttpService() {
|
||||
lastTime := int64(0)
|
||||
for {
|
||||
nowTime := time.Now().UnixNano()
|
||||
if time.Duration(nowTime-lastTime) < time.Second {
|
||||
time.Sleep(time.Millisecond * 100)
|
||||
continue
|
||||
}
|
||||
lastTime = time.Now().UnixNano()
|
||||
var responseData *airClient.ResponseData
|
||||
var err error
|
||||
responseData, err = airClient.PollAllHttpService()
|
||||
if err != nil {
|
||||
logger.LOG.Error("poll all http service error: %v", err)
|
||||
continue
|
||||
}
|
||||
a.syncServiceMap(responseData)
|
||||
a.serviceAddressMap.Lock.RLock()
|
||||
logger.LOG.Debug("poll finished, serviceAddressMap: %v", a.serviceAddressMap.Map)
|
||||
a.serviceAddressMap.Lock.RUnlock()
|
||||
}
|
||||
}
|
||||
31
wind/cmd/application.toml
Normal file
31
wind/cmd/application.toml
Normal file
@@ -0,0 +1,31 @@
|
||||
http_port = 80
|
||||
|
||||
[logger]
|
||||
level = "DEBUG"
|
||||
method = "CONSOLE"
|
||||
track_line = true
|
||||
|
||||
[air]
|
||||
addr = "air"
|
||||
port = 8086
|
||||
service_name = "wind-gateway"
|
||||
|
||||
[[routes]]
|
||||
service_name = "water-auth"
|
||||
service_predicates = "/api/v1/auth"
|
||||
strip_prefix = 2
|
||||
|
||||
[[routes]]
|
||||
service_name = "annie-user-app"
|
||||
service_predicates = "/api/v1/user"
|
||||
strip_prefix = 2
|
||||
|
||||
[[routes]]
|
||||
service_name = "annie-wxmp-app"
|
||||
service_predicates = "/api/v1/wxmp"
|
||||
strip_prefix = 2
|
||||
|
||||
[[routes]]
|
||||
service_name = "gm-hk4e-app"
|
||||
service_predicates = "/api/v1/gm"
|
||||
strip_prefix = 2
|
||||
44
wind/cmd/main.go
Normal file
44
wind/cmd/main.go
Normal file
@@ -0,0 +1,44 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"flswld.com/common/config"
|
||||
"flswld.com/logger"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
"time"
|
||||
"wind/air"
|
||||
"wind/entity"
|
||||
"wind/proxy"
|
||||
)
|
||||
|
||||
func main() {
|
||||
filePath := "./application.toml"
|
||||
config.InitConfig(filePath)
|
||||
|
||||
logger.InitLogger()
|
||||
logger.LOG.Info("wind start")
|
||||
|
||||
svcAddrMap := new(entity.AddressMap)
|
||||
svcAddrMap.Map = make(map[string][]string)
|
||||
|
||||
_ = air.NewAir(svcAddrMap)
|
||||
|
||||
_ = proxy.NewProxy(svcAddrMap)
|
||||
|
||||
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("wind exit")
|
||||
time.Sleep(time.Second)
|
||||
return
|
||||
case syscall.SIGHUP:
|
||||
default:
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
9
wind/entity/service.go
Normal file
9
wind/entity/service.go
Normal file
@@ -0,0 +1,9 @@
|
||||
package entity
|
||||
|
||||
import "sync"
|
||||
|
||||
// 服务列表
|
||||
type AddressMap struct {
|
||||
Map map[string][]string
|
||||
Lock sync.RWMutex
|
||||
}
|
||||
17
wind/go.mod
Normal file
17
wind/go.mod
Normal file
@@ -0,0 +1,17 @@
|
||||
module wind
|
||||
|
||||
go 1.19
|
||||
|
||||
require flswld.com/common v0.0.0-incompatible
|
||||
|
||||
replace flswld.com/common => ../common
|
||||
|
||||
require flswld.com/logger v0.0.0-incompatible
|
||||
|
||||
require github.com/BurntSushi/toml v0.3.1 // indirect
|
||||
|
||||
replace flswld.com/logger => ../logger
|
||||
|
||||
require flswld.com/air-api v0.0.0-incompatible
|
||||
|
||||
replace flswld.com/air-api => ../air-api
|
||||
2
wind/go.sum
Normal file
2
wind/go.sum
Normal file
@@ -0,0 +1,2 @@
|
||||
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
119
wind/proxy/proxy.go
Normal file
119
wind/proxy/proxy.go
Normal file
@@ -0,0 +1,119 @@
|
||||
package proxy
|
||||
|
||||
import (
|
||||
"flswld.com/common/config"
|
||||
"flswld.com/logger"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httputil"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"wind/entity"
|
||||
)
|
||||
|
||||
type Proxy struct {
|
||||
service Service
|
||||
}
|
||||
|
||||
// 服务
|
||||
type Service struct {
|
||||
// 服务地址列表map
|
||||
serviceAddrMap *entity.AddressMap
|
||||
// 服务负载均衡索引map
|
||||
serviceLoadBalanceIndexMap map[string]int
|
||||
serviceLoadBalanceIndexMapLock sync.Mutex
|
||||
}
|
||||
|
||||
func NewProxy(addressMap *entity.AddressMap) (r *Proxy) {
|
||||
r = new(Proxy)
|
||||
r.service.serviceAddrMap = addressMap
|
||||
go r.startServer()
|
||||
return r
|
||||
}
|
||||
|
||||
// 路由转发处理
|
||||
func (p *Proxy) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
logger.LOG.Debug("[proxy:ServeHTTP] Request: %v", *r)
|
||||
urlParamDiv := strings.Index(r.RequestURI, "?")
|
||||
var reqUrl string
|
||||
if urlParamDiv != -1 {
|
||||
reqUrl = (r.RequestURI)[:urlParamDiv]
|
||||
} else {
|
||||
reqUrl = r.RequestURI
|
||||
}
|
||||
var svcNameStr = ""
|
||||
var stripPrefix = 0
|
||||
// 匹配服务
|
||||
for _, v := range config.CONF.Routes {
|
||||
if strings.Contains(reqUrl, v.ServicePredicates) {
|
||||
svcNameStr = v.ServiceName
|
||||
stripPrefix = v.StripPrefix
|
||||
break
|
||||
}
|
||||
}
|
||||
// 匹配服务失败
|
||||
if len(svcNameStr) == 0 {
|
||||
logger.LOG.Info("[proxy:ServeHTTP] 404 Not Found")
|
||||
_, _ = fmt.Fprintf(w, "404 Not Found")
|
||||
return
|
||||
}
|
||||
path := reqUrl
|
||||
// 去除路径前缀
|
||||
for i := 0; i < stripPrefix; i++ {
|
||||
path = path[strings.Index(path, "/")+1:]
|
||||
path = path[strings.Index(path, "/"):]
|
||||
}
|
||||
r.URL.Path = path
|
||||
var remote *url.URL
|
||||
p.service.serviceAddrMap.Lock.RLock()
|
||||
serviceAddr := p.service.serviceAddrMap.Map[svcNameStr]
|
||||
p.service.serviceAddrMap.Lock.RUnlock()
|
||||
// 服务不可用
|
||||
if len(serviceAddr) == 0 {
|
||||
logger.LOG.Info("[proxy:ServeHTTP] 503 Service Unavailable")
|
||||
_, _ = fmt.Fprintf(w, "503 Service Unavailable")
|
||||
return
|
||||
}
|
||||
p.service.serviceLoadBalanceIndexMapLock.Lock()
|
||||
serviceLoadBalanceIndex := p.service.serviceLoadBalanceIndexMap[svcNameStr]
|
||||
p.service.serviceLoadBalanceIndexMapLock.Unlock()
|
||||
// 下一个待轮询的服务已下线
|
||||
if int(serviceLoadBalanceIndex) >= len(serviceAddr) {
|
||||
logger.LOG.Info("[proxy:ServeHTTP] serviceLoadBalanceIndex out of range, len is: %d, but value is: %d", len(serviceAddr), serviceLoadBalanceIndex)
|
||||
serviceLoadBalanceIndex = 0
|
||||
}
|
||||
logger.LOG.Debug("[proxy:ServeHTTP] serviceLoadBalanceIndex: %d", serviceLoadBalanceIndex)
|
||||
remote, _ = url.Parse(string(serviceAddr[serviceLoadBalanceIndex]))
|
||||
logger.LOG.Debug("[proxy:ServeHTTP] remote: %v", remote)
|
||||
// 轮询
|
||||
p.service.serviceLoadBalanceIndexMapLock.Lock()
|
||||
if int(p.service.serviceLoadBalanceIndexMap[svcNameStr]) < len(serviceAddr)-1 {
|
||||
p.service.serviceLoadBalanceIndexMap[svcNameStr] += 1
|
||||
} else {
|
||||
p.service.serviceLoadBalanceIndexMap[svcNameStr] = 0
|
||||
}
|
||||
p.service.serviceLoadBalanceIndexMapLock.Unlock()
|
||||
proxy := httputil.NewSingleHostReverseProxy(remote)
|
||||
//p.log.Debug("Response: %v", w)
|
||||
proxy.ServeHTTP(w, r)
|
||||
}
|
||||
|
||||
// 启动http路由转发
|
||||
func (p *Proxy) startServer() {
|
||||
// 初始化服务负载均衡索引map
|
||||
p.service.serviceLoadBalanceIndexMap = make(map[string]int)
|
||||
for _, v := range config.CONF.Routes {
|
||||
p.service.serviceLoadBalanceIndexMap[v.ServiceName] = 0
|
||||
}
|
||||
port := strconv.FormatInt(int64(config.CONF.HttpPort), 10)
|
||||
logger.LOG.Info("[proxy:startServer] start listen port: %s", port)
|
||||
portStr := ":" + port
|
||||
// 启动
|
||||
err := http.ListenAndServe(portStr, p)
|
||||
if err != nil {
|
||||
logger.LOG.Error("[proxy:startServer] ListenAndServe fail ! err: %v", err)
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user