blob: 7a174f30b7d8757fb385aa5f6041cf8090a5fa92 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package meshrouter
import (
"math/rand"
)
import (
"github.com/dubbogo/gost/log/logger"
)
import (
"dubbo.apache.org/dubbo-go/v3/cluster/router"
"dubbo.apache.org/dubbo-go/v3/common"
"dubbo.apache.org/dubbo-go/v3/common/constant"
"dubbo.apache.org/dubbo-go/v3/config_center"
"dubbo.apache.org/dubbo-go/v3/protocol"
"dubbo.apache.org/dubbo-go/v3/remoting/xds"
)
const (
name = "mesh-router"
)
// MeshRouter have
type MeshRouter struct {
client *xds.WrappedClientImpl
}
// NewMeshRouter construct an NewConnCheckRouter via url
func NewMeshRouter() (router.PriorityRouter, error) {
xdsWrappedClient := xds.GetXDSWrappedClient()
if xdsWrappedClient == nil {
logger.Debugf("[Mesh Router] xds wrapped client is not created.")
}
return &MeshRouter{
client: xdsWrappedClient,
}, nil
}
// Route gets a list of routed invoker
func (r *MeshRouter) Route(invokers []protocol.Invoker, url *common.URL, invocation protocol.Invocation) []protocol.Invoker {
if r.client == nil {
return invokers
}
hostAddr, err := r.client.GetHostAddrByServiceUniqueKey(common.GetSubscribeName(url))
if err != nil {
// todo deal with error
return nil
}
rconf := r.client.GetRouterConfig(hostAddr)
clusterInvokerMap := make(map[string][]protocol.Invoker)
for _, v := range invokers {
meshClusterID := v.GetURL().GetParam(constant.MeshClusterIDKey, "")
if _, ok := clusterInvokerMap[meshClusterID]; !ok {
clusterInvokerMap[meshClusterID] = make([]protocol.Invoker, 0)
}
clusterInvokerMap[meshClusterID] = append(clusterInvokerMap[meshClusterID], v)
}
route, err := r.client.MatchRoute(rconf, invocation)
if err != nil {
logger.Errorf("[Mesh Router] not found route,method=%s", invocation.MethodName())
return nil
}
// Loop through routes in order and select first match.
if route == nil || route.WeightedClusters == nil {
logger.Errorf("[Mesh Router] route's WeightedClusters is empty, route: %+v", r)
return invokers
}
invokersWeightPairs := make(invokerWeightPairs, 0)
for clusterID, weight := range route.WeightedClusters {
// cluster -> invokers
targetInvokers := clusterInvokerMap[clusterID]
invokersWeightPairs = append(invokersWeightPairs, invokerWeightPair{
invokers: targetInvokers,
weight: weight.Weight,
})
}
return invokersWeightPairs.GetInvokers()
}
// Process there is no process needs for uniform Router, as it upper struct RouterChain has done it
func (r *MeshRouter) Process(event *config_center.ConfigChangeEvent) {
}
// Name get name of ConnCheckerRouter
func (r *MeshRouter) Name() string {
return name
}
// Priority get Router priority level
func (r *MeshRouter) Priority() int64 {
return 0
}
// URL Return URL in router
func (r *MeshRouter) URL() *common.URL {
return nil
}
// Notify the router the invoker list
func (r *MeshRouter) Notify(invokers []protocol.Invoker) {
}
type invokerWeightPair struct {
invokers []protocol.Invoker
weight uint32
}
type invokerWeightPairs []invokerWeightPair
func (i *invokerWeightPairs) GetInvokers() []protocol.Invoker {
if len(*i) == 0 {
return nil
}
totalWeight := uint32(0)
tempWeight := uint32(0)
for _, v := range *i {
totalWeight += v.weight
}
randFloat := rand.Float64()
for _, v := range *i {
tempWeight += v.weight
tempPercent := float64(tempWeight) / float64(totalWeight)
if tempPercent >= randFloat {
return v.invokers
}
}
return (*i)[0].invokers
}