|
|
|
package polaris
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"fmt"
|
|
|
|
"net"
|
|
|
|
"strconv"
|
|
|
|
"strings"
|
|
|
|
|
|
|
|
"github.com/polarismesh/polaris-go"
|
|
|
|
"github.com/polarismesh/polaris-go/pkg/model"
|
|
|
|
"github.com/polarismesh/polaris-go/pkg/model/local"
|
|
|
|
"github.com/polarismesh/polaris-go/pkg/model/pb"
|
|
|
|
v1 "github.com/polarismesh/polaris-go/pkg/model/pb/v1"
|
|
|
|
"google.golang.org/protobuf/types/known/wrapperspb"
|
|
|
|
|
|
|
|
"github.com/go-kratos/kratos/v2"
|
|
|
|
"github.com/go-kratos/kratos/v2/log"
|
|
|
|
"github.com/go-kratos/kratos/v2/selector"
|
|
|
|
"github.com/go-kratos/kratos/v2/transport"
|
|
|
|
"github.com/go-kratos/kratos/v2/transport/http"
|
|
|
|
)
|
|
|
|
|
|
|
|
type router struct {
|
|
|
|
service string
|
|
|
|
}
|
|
|
|
|
|
|
|
type RouterOption func(o *router)
|
|
|
|
|
|
|
|
// WithRouterService set the caller service name used by the route
|
|
|
|
func WithRouterService(service string) RouterOption {
|
|
|
|
return func(o *router) {
|
|
|
|
o.service = service
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// NodeFilter polaris dynamic router selector
|
|
|
|
func (p *Polaris) NodeFilter(opts ...RouterOption) selector.NodeFilter {
|
|
|
|
o := router{service: p.service}
|
|
|
|
for _, opt := range opts {
|
|
|
|
opt(&o)
|
|
|
|
}
|
|
|
|
return func(ctx context.Context, nodes []selector.Node) []selector.Node {
|
|
|
|
if len(nodes) == 0 {
|
|
|
|
return nodes
|
|
|
|
}
|
|
|
|
req := &polaris.ProcessRoutersRequest{
|
|
|
|
ProcessRoutersRequest: model.ProcessRoutersRequest{
|
|
|
|
SourceService: model.ServiceInfo{Namespace: p.namespace, Service: o.service},
|
|
|
|
DstInstances: buildPolarisInstance(p.namespace, nodes),
|
|
|
|
},
|
|
|
|
}
|
|
|
|
if appInfo, ok := kratos.FromContext(ctx); ok {
|
|
|
|
req.SourceService.Service = appInfo.Name()
|
|
|
|
}
|
|
|
|
|
|
|
|
req.AddArguments(model.BuildCallerServiceArgument(p.namespace, req.ProcessRoutersRequest.SourceService.Service))
|
|
|
|
|
|
|
|
// process transport
|
|
|
|
if tr, ok := transport.FromClientContext(ctx); ok {
|
|
|
|
req.AddArguments(model.BuildMethodArgument(tr.Operation()))
|
|
|
|
req.AddArguments(model.BuildPathArgument(tr.Operation()))
|
|
|
|
|
|
|
|
for _, key := range tr.RequestHeader().Keys() {
|
|
|
|
req.AddArguments(model.BuildHeaderArgument(strings.ToLower(key), tr.RequestHeader().Get(key)))
|
|
|
|
}
|
|
|
|
|
|
|
|
// http
|
|
|
|
if ht, ok := tr.(http.Transporter); ok {
|
|
|
|
req.AddArguments(model.BuildPathArgument(ht.Request().URL.Path))
|
|
|
|
req.AddArguments(model.BuildCallerIPArgument(ht.Request().RemoteAddr))
|
|
|
|
|
|
|
|
// cookie
|
|
|
|
for _, cookie := range ht.Request().Cookies() {
|
|
|
|
req.AddArguments(model.BuildCookieArgument(cookie.Name, cookie.Value))
|
|
|
|
}
|
|
|
|
|
|
|
|
// url query
|
|
|
|
for key, values := range ht.Request().URL.Query() {
|
|
|
|
req.AddArguments(model.BuildQueryArgument(key, strings.Join(values, ",")))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
n := make(map[string]selector.Node, len(nodes))
|
|
|
|
|
|
|
|
for _, node := range nodes {
|
|
|
|
n[node.Address()] = node
|
|
|
|
}
|
|
|
|
|
|
|
|
m, err := p.router.ProcessRouters(req)
|
|
|
|
if err != nil {
|
|
|
|
log.Errorf("polaris process routers failed, err=%v", err)
|
|
|
|
return nodes
|
|
|
|
}
|
|
|
|
|
|
|
|
newNode := make([]selector.Node, 0, len(m.Instances))
|
|
|
|
for _, ins := range m.GetInstances() {
|
|
|
|
if v, ok := n[fmt.Sprintf("%s:%d", ins.GetHost(), ins.GetPort())]; ok {
|
|
|
|
newNode = append(newNode, v)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if len(newNode) == 0 {
|
|
|
|
return nodes
|
|
|
|
}
|
|
|
|
return newNode
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func buildPolarisInstance(namespace string, nodes []selector.Node) *pb.ServiceInstancesInProto {
|
|
|
|
ins := make([]*v1.Instance, 0, len(nodes))
|
|
|
|
for _, node := range nodes {
|
|
|
|
host, port, err := net.SplitHostPort(node.Address())
|
|
|
|
if err != nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
portInt, err := strconv.Atoi(port)
|
|
|
|
if err != nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
ins = append(ins, &v1.Instance{
|
|
|
|
Id: wrapperspb.String(node.Metadata()["merge"]),
|
|
|
|
Service: wrapperspb.String(node.ServiceName()),
|
|
|
|
Namespace: wrapperspb.String(namespace),
|
|
|
|
Host: wrapperspb.String(host),
|
|
|
|
Port: wrapperspb.UInt32(uint32(portInt)),
|
|
|
|
Protocol: wrapperspb.String(node.Scheme()),
|
|
|
|
Version: wrapperspb.String(node.Version()),
|
|
|
|
Weight: wrapperspb.UInt32(uint32(*node.InitialWeight())),
|
|
|
|
Metadata: node.Metadata(),
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
d := &v1.DiscoverResponse{
|
|
|
|
Code: wrapperspb.UInt32(1),
|
|
|
|
Info: wrapperspb.String("ok"),
|
|
|
|
Type: v1.DiscoverResponse_INSTANCE,
|
|
|
|
Service: &v1.Service{Name: wrapperspb.String(nodes[0].ServiceName()), Namespace: wrapperspb.String("default")},
|
|
|
|
Instances: ins,
|
|
|
|
}
|
|
|
|
return pb.NewServiceInstancesInProto(d, func(s string) local.InstanceLocalValue {
|
|
|
|
return local.NewInstanceLocalValue()
|
|
|
|
}, &pb.SvcPluginValues{Routers: nil, Loadbalancer: nil}, nil)
|
|
|
|
}
|