Skip to content

Commit d7b32c4

Browse files
committed
feat: preliminary implementation of traffic routing
1 parent 0807185 commit d7b32c4

16 files changed

+640
-0
lines changed

core/route/base/headers.go

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package base
2+
3+
type Headers struct {
4+
Request *HeaderOperations
5+
Response *HeaderOperations
6+
}
7+
8+
type HeaderOperations struct {
9+
Set map[string]string
10+
Add map[string]string
11+
Remove []string
12+
}

core/route/base/http_match_request.go

+61
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
package base
2+
3+
import (
4+
"net/url"
5+
"strconv"
6+
)
7+
8+
type HTTPMatchRequest struct {
9+
Name string
10+
Headers map[string]*StringMatch
11+
Uri *StringMatch
12+
Scheme *StringMatch
13+
Authority *StringMatch
14+
Method *StringMatch
15+
Port *int
16+
QueryParams map[string]*StringMatch
17+
}
18+
19+
// TODO
20+
func (h *HTTPMatchRequest) IsMatch(context *TrafficContext) bool {
21+
22+
for key, match := range h.Headers {
23+
if v, ok := context.Headers[key]; ok && !match.IsMatch(v) {
24+
return false
25+
}
26+
}
27+
28+
if h.Uri != nil && !h.Uri.IsMatch(context.Uri) {
29+
return false
30+
}
31+
32+
var parsedURL *url.URL
33+
var err error
34+
35+
if h.Uri != nil || h.Scheme != nil || h.Authority != nil || h.Port != nil {
36+
parsedURL, err = url.Parse(context.Uri)
37+
if err != nil {
38+
return false
39+
}
40+
}
41+
if h.Uri != nil && !h.Uri.IsMatch(parsedURL.Path) {
42+
return false
43+
}
44+
if h.Scheme != nil && !h.Scheme.IsMatch(parsedURL.Scheme) {
45+
return false
46+
}
47+
if h.Authority != nil && !h.Authority.IsMatch(parsedURL.Host) {
48+
return false
49+
}
50+
if h.Port != nil {
51+
p, err := strconv.Atoi(parsedURL.Port())
52+
if err != nil || *h.Port != p {
53+
return false
54+
}
55+
}
56+
if !h.Method.IsMatch(context.MethodName) {
57+
return false
58+
}
59+
60+
return true
61+
}

core/route/base/http_route.go

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package base
2+
3+
type HTTPRoute struct {
4+
Name string
5+
Match []*HTTPMatchRequest
6+
Route []*HTTPRouteDestination
7+
}
8+
9+
func (h *HTTPRoute) IsMatch(context *TrafficContext) bool {
10+
for _, match := range h.Match {
11+
if match.IsMatch(context) {
12+
return true
13+
}
14+
}
15+
return false
16+
}
+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package base
2+
3+
import "fmt"
4+
5+
type HTTPRouteDestination struct {
6+
Weight int
7+
Destination *Destination
8+
Headers Headers // TODO modifies headers
9+
}
10+
11+
func (H HTTPRouteDestination) String() string {
12+
return fmt.Sprintf("{Weight: %v, Destination: %+v}\n", H.Weight, H.Destination)
13+
}
14+
15+
type Destination struct {
16+
Host string
17+
Subset string
18+
Port uint32
19+
Fallback *HTTPRouteDestination
20+
}

core/route/base/instance.go

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package base
2+
3+
type Instance struct {
4+
AppName string
5+
Host string
6+
Port int
7+
Metadata map[string]string
8+
TargetInstance interface{}
9+
}

core/route/base/string_match.go

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package base
2+
3+
import "regexp"
4+
5+
type StringMatch struct {
6+
Exact string
7+
Prefix string
8+
Regex string
9+
}
10+
11+
func (s *StringMatch) IsMatch(input string) bool {
12+
if input == "" {
13+
return false
14+
}
15+
16+
if s.Exact != "" {
17+
return input == s.Exact
18+
} else if s.Prefix != "" {
19+
return len(input) >= len(s.Prefix) && input[:len(s.Prefix)] == s.Prefix
20+
} else if s.Regex != "" {
21+
matched, _ := regexp.MatchString(s.Regex, input)
22+
return matched
23+
}
24+
return true
25+
}

core/route/base/traffic_context.go

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package base
2+
3+
type TrafficContext struct {
4+
Path string
5+
Uri string
6+
ServiceName string
7+
Group string
8+
Version string
9+
MethodName string
10+
ParamTypes []string
11+
Args []interface{}
12+
Headers map[string]string
13+
Baggage map[string]string
14+
}

core/route/base/traffic_policy.go

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package base
2+
3+
type TrafficPolicy struct {
4+
LoadBalancer string
5+
}

core/route/base/traffic_router.go

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package base
2+
3+
type TrafficRouter struct {
4+
Host []string
5+
Http []*HTTPRoute
6+
}
7+
8+
type Fallback struct {
9+
Host string
10+
Subset string
11+
}

core/route/base/virtual_workload.go

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package base
2+
3+
type VirtualWorkload struct {
4+
Host string
5+
trafficPolicy *TrafficPolicy
6+
Subsets []*Subset
7+
}
8+
9+
type Subset struct {
10+
Name string
11+
Labels map[string]string
12+
}

core/route/cluster_manager.go

+51
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
package route
2+
3+
import (
4+
"github.com/alibaba/sentinel-golang/core/route/base"
5+
"github.com/pkg/errors"
6+
)
7+
8+
type ClusterManager struct {
9+
InstanceManager InstanceManager
10+
RouterFilterList []RouterFilter
11+
LoadBalancer LoadBalancer
12+
}
13+
14+
func NewClusterManager(instanceManager InstanceManager, routerFilters []RouterFilter, loadBalancer LoadBalancer) *ClusterManager {
15+
return &ClusterManager{
16+
InstanceManager: instanceManager,
17+
RouterFilterList: routerFilters,
18+
LoadBalancer: loadBalancer,
19+
}
20+
}
21+
22+
func (m *ClusterManager) Route(context *base.TrafficContext) ([]*base.Instance, error) {
23+
instances := m.InstanceManager.GetInstances()
24+
25+
var err error
26+
for _, routerFilter := range m.RouterFilterList {
27+
instances, err = routerFilter.Filter(instances, context)
28+
if err != nil {
29+
return nil, err
30+
}
31+
}
32+
if len(instances) == 0 {
33+
return nil, errors.New("no matching instances")
34+
}
35+
return instances, nil
36+
}
37+
38+
func (m *ClusterManager) GetOne(context *base.TrafficContext) (*base.Instance, error) {
39+
instances, err := m.Route(context)
40+
if err != nil {
41+
return nil, err
42+
}
43+
if m.LoadBalancer == nil {
44+
return instances[0], nil
45+
}
46+
instance, err := m.LoadBalancer.Select(instances, context)
47+
if err != nil {
48+
return nil, err
49+
}
50+
return instance, nil
51+
}

core/route/instance_manager.go

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package route
2+
3+
import "github.com/alibaba/sentinel-golang/core/route/base"
4+
5+
type InstanceManager interface {
6+
StoreInstances(instances []*base.Instance)
7+
GetInstances() []*base.Instance
8+
}
9+
10+
type BasicInstanceManager struct {
11+
Instances []*base.Instance
12+
}
13+
14+
func NewBasicInstanceManager() *BasicInstanceManager {
15+
return &BasicInstanceManager{}
16+
}
17+
18+
func (b *BasicInstanceManager) StoreInstances(instances []*base.Instance) {
19+
b.Instances = instances
20+
}
21+
22+
func (b *BasicInstanceManager) GetInstances() []*base.Instance {
23+
return b.Instances
24+
}

core/route/load_balancer.go

+47
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
package route
2+
3+
import (
4+
"github.com/alibaba/sentinel-golang/core/route/base"
5+
"math/rand"
6+
"sync"
7+
)
8+
9+
type LoadBalancer interface {
10+
Select(instances []*base.Instance, context *base.TrafficContext) (*base.Instance, error)
11+
}
12+
13+
type RandomLoadBalancer struct {
14+
}
15+
16+
func NewRandomLoadBalancer() *RandomLoadBalancer {
17+
return &RandomLoadBalancer{}
18+
}
19+
20+
func (r *RandomLoadBalancer) Select(instances []*base.Instance, context *base.TrafficContext) (*base.Instance, error) {
21+
if len(instances) == 0 {
22+
return nil, nil
23+
}
24+
25+
return instances[rand.Intn(len(instances))], nil
26+
}
27+
28+
type RoundRobinLoadBalancer struct {
29+
idx int
30+
mu sync.Mutex
31+
}
32+
33+
func NewRoundRobinLoadBalancer() *RoundRobinLoadBalancer {
34+
return &RoundRobinLoadBalancer{idx: 0}
35+
}
36+
37+
func (r *RoundRobinLoadBalancer) Select(instances []*base.Instance, context *base.TrafficContext) (*base.Instance, error) {
38+
if len(instances) == 0 {
39+
return nil, nil
40+
}
41+
42+
r.mu.Lock()
43+
defer r.mu.Unlock()
44+
45+
r.idx = (r.idx + 1) % len(instances)
46+
return instances[r.idx], nil
47+
}

0 commit comments

Comments
 (0)