181 lines
4.8 KiB
Go
181 lines
4.8 KiB
Go
package rpcx
|
|
|
|
import (
|
|
"context"
|
|
"go_dreamfactory/lego/sys/log"
|
|
"regexp"
|
|
"strings"
|
|
"sync"
|
|
|
|
"github.com/smallnest/rpcx/share"
|
|
"github.com/valyala/fastrand"
|
|
)
|
|
|
|
var rex_nogather = regexp.MustCompile(`\!\[([^)]+)\]`)
|
|
var rex_noid = regexp.MustCompile(`\!([^)]+)`)
|
|
var rex_gather = regexp.MustCompile(`\[([^)]+)\]`)
|
|
|
|
func newSelector(log log.ILogger, stag string, fn func(map[string]*ServiceNode)) *Selector {
|
|
return &Selector{
|
|
log: log,
|
|
stag: stag,
|
|
updateServerEvent: fn,
|
|
servers: make(map[string]*ServiceNode),
|
|
serversType: make(map[string][]*ServiceNode),
|
|
i: make(map[string]int),
|
|
}
|
|
}
|
|
|
|
type ServiceNode struct {
|
|
ServiceTag string `json:"stag"` //服务集群标签
|
|
ServiceId string `json:"sid"` //服务id
|
|
ServiceType string `json:"stype"` //服务类型
|
|
Version string `json:"version"` //服务版本
|
|
ServiceAddr string `json:"addr"` //服务地址
|
|
}
|
|
|
|
type Selector struct {
|
|
log log.ILogger
|
|
stag string
|
|
updateServerEvent func(map[string]*ServiceNode)
|
|
servers map[string]*ServiceNode
|
|
serversType map[string][]*ServiceNode
|
|
lock sync.Mutex
|
|
i map[string]int
|
|
}
|
|
|
|
///servicePath = (worker)/(worker/worker_1)/(worker/!worker_1)/(worker/[worker_1,worker_2])/(worker/![worker_1,worker_2])
|
|
func (this *Selector) Select(ctx context.Context, servicePath, serviceMethod string, args interface{}) string {
|
|
routrules := ctx.Value(share.ReqMetaDataKey).(map[string]string)[CallRoutRulesKey]
|
|
service := strings.Split(routrules, "/")
|
|
leng := len(service)
|
|
if leng == 1 {
|
|
if nodes, ok := this.serversType[service[0]]; ok {
|
|
i, ok := this.i[service[0]]
|
|
if !ok {
|
|
i = 0
|
|
}
|
|
i = i % len(nodes)
|
|
this.lock.Lock()
|
|
this.i[service[0]] = i + 1
|
|
this.lock.Unlock()
|
|
return nodes[i].ServiceAddr
|
|
}
|
|
} else if leng == 2 {
|
|
result := this.ParseRoutRules(service[1])
|
|
if len(result) == 0 {
|
|
this.log.Error("Select no found any node",
|
|
log.Field{Key: "stag", Value: this.stag},
|
|
log.Field{Key: "servicePath", Value: servicePath},
|
|
log.Field{Key: "serviceMethod", Value: serviceMethod},
|
|
log.Field{Key: "routrules", Value: routrules},
|
|
)
|
|
return ""
|
|
}
|
|
i := fastrand.Uint32n(uint32(len(result)))
|
|
if node, ok := this.servers[result[i]]; ok {
|
|
return node.ServiceAddr
|
|
}
|
|
}
|
|
// this.log.Error("Select no found any node", log.Field{"stag", this.stag}, log.Field{"servicePath", servicePath}, log.Field{"serviceMethod", serviceMethod}, log.Field{"routrules", routrules})
|
|
return ""
|
|
}
|
|
|
|
//找到同类型节点信息
|
|
func (this *Selector) Find(ctx context.Context, servicePath, serviceMethod string, args interface{}) []string {
|
|
if nodes, ok := this.serversType[servicePath]; ok {
|
|
addrs := make([]string, len(nodes))
|
|
for i, v := range nodes {
|
|
addrs[i] = v.ServiceAddr
|
|
}
|
|
return addrs
|
|
}
|
|
return nil
|
|
}
|
|
|
|
//更新服务列表
|
|
func (this *Selector) UpdateServer(servers map[string]string) {
|
|
ss := make(map[string]*ServiceNode)
|
|
sst := make(map[string][]*ServiceNode)
|
|
for _, v := range servers {
|
|
if node, err := smetaToServiceNode(v); err != nil {
|
|
this.log.Errorf("smetaToServiceNode:%s err:%v", v, err)
|
|
continue
|
|
} else {
|
|
ss[node.ServiceId] = node
|
|
if ssts, ok := sst[node.ServiceType]; !ok {
|
|
sst[node.ServiceType] = make([]*ServiceNode, 0)
|
|
sst[node.ServiceType] = append(sst[node.ServiceType], node)
|
|
} else {
|
|
ssts = append(ssts, node)
|
|
}
|
|
}
|
|
|
|
}
|
|
this.servers = ss
|
|
this.serversType = sst
|
|
if this.updateServerEvent != nil {
|
|
go this.updateServerEvent(ss)
|
|
}
|
|
}
|
|
|
|
//路由规则解析
|
|
func (this *Selector) ParseRoutRules(rules string) (result []string) {
|
|
result = make([]string, 0)
|
|
|
|
//解析 ![sid,sid] 格式规则
|
|
if out := rex_nogather.FindAllStringSubmatch(rules, -1); len(out) == 1 && len(out[0]) == 2 {
|
|
if nogather := strings.Split(out[0][1], ","); len(nogather) > 0 {
|
|
for k, _ := range this.servers {
|
|
iskeep := false
|
|
for _, v := range nogather {
|
|
if k == v {
|
|
iskeep = true
|
|
break
|
|
}
|
|
}
|
|
if !iskeep {
|
|
result = append(result, k)
|
|
}
|
|
}
|
|
return
|
|
}
|
|
}
|
|
//解析 !sid 格式规则
|
|
if out := rex_noid.FindAllStringSubmatch(rules, -1); len(out) == 1 && len(out[0]) == 2 {
|
|
for k, _ := range this.servers {
|
|
iskeep := false
|
|
if k == out[0][1] {
|
|
iskeep = true
|
|
break
|
|
}
|
|
if !iskeep {
|
|
result = append(result, k)
|
|
}
|
|
}
|
|
return
|
|
}
|
|
//解析 [sid,sid] 格式规则
|
|
if out := rex_gather.FindAllStringSubmatch(rules, -1); len(out) == 1 && len(out[0]) == 2 {
|
|
if nogather := strings.Split(out[0][1], ","); len(nogather) > 0 {
|
|
for k, _ := range this.servers {
|
|
iskeep := false
|
|
for _, v := range nogather {
|
|
if k == v {
|
|
iskeep = true
|
|
break
|
|
}
|
|
}
|
|
if iskeep {
|
|
result = append(result, k)
|
|
}
|
|
}
|
|
return
|
|
}
|
|
}
|
|
if _, ok := this.servers[rules]; ok {
|
|
result = append(result, rules)
|
|
}
|
|
return
|
|
}
|