You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@dubbo.apache.org by al...@apache.org on 2025/07/13 07:14:35 UTC
(dubbo-go) branch develop updated: feat: Refactor weight handling in loadbalance and service discovery (#2937)
This is an automated email from the ASF dual-hosted git repository.
alexstocks pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/dubbo-go.git
The following commit(s) were added to refs/heads/develop by this push:
new f39795d37 feat: Refactor weight handling in loadbalance and service discovery (#2937)
f39795d37 is described below
commit f39795d3735617e114e9c92427ff0b2851379729
Author: john <32...@users.noreply.github.com>
AuthorDate: Sun Jul 13 15:14:29 2025 +0800
feat: Refactor weight handling in loadbalance and service discovery (#2937)
- Convert Nacos weight from float64 to int64 (with rounding)
- Refactor weight injection as Option to eliminate ToURLs duplication
- Optimize loadbalance weight processing for better performance
- Format code layout and clean up comments for better readability
- Improve test cases with better naming conventions
- Fix formatting issues in service configuration and URL handling
Co-authored-by: john <>
---
cluster/loadbalance/p2c/loadbalance_test.go | 11 +---
cluster/loadbalance/util.go | 50 +++++++++--------
common/url.go | 18 +++---
global/service_config.go | 2 +
registry/nacos/service_discovery.go | 44 +++++++++------
registry/nacos/service_discovery_test.go | 87 ++++++++++++++++++++++++++---
registry/service_instance.go | 23 +++++---
server/action.go | 2 +
8 files changed, 166 insertions(+), 71 deletions(-)
diff --git a/cluster/loadbalance/p2c/loadbalance_test.go b/cluster/loadbalance/p2c/loadbalance_test.go
index 489e0a66e..1f4fa034d 100644
--- a/cluster/loadbalance/p2c/loadbalance_test.go
+++ b/cluster/loadbalance/p2c/loadbalance_test.go
@@ -20,18 +20,13 @@ package p2c
import (
"math/rand"
"testing"
-)
-
-import (
- "github.com/golang/mock/gomock"
- "github.com/stretchr/testify/assert"
-)
-
-import (
"dubbo.apache.org/dubbo-go/v3/cluster/metrics"
"dubbo.apache.org/dubbo-go/v3/common"
"dubbo.apache.org/dubbo-go/v3/protocol/base"
+ "github.com/golang/mock/gomock"
+ "github.com/stretchr/testify/assert"
+
protoinvoc "dubbo.apache.org/dubbo-go/v3/protocol/invocation"
)
diff --git a/cluster/loadbalance/util.go b/cluster/loadbalance/util.go
index 5de8db5ae..2eb76fe47 100644
--- a/cluster/loadbalance/util.go
+++ b/cluster/loadbalance/util.go
@@ -19,36 +19,41 @@ package loadbalance
import (
"time"
-)
-import (
"dubbo.apache.org/dubbo-go/v3/common/constant"
"dubbo.apache.org/dubbo-go/v3/protocol/base"
)
-// GetWeight gets weight for load balance strategy
+// GetWeight returns the weight for the load‑balancing strategy.
func GetWeight(invoker base.Invoker, invocation base.Invocation) int64 {
- var weight int64
+
url := invoker.GetURL()
- // Multiple registry scenario, load balance among multiple registries.
- isRegIvk := url.GetParamBool(constant.RegistryKey+"."+constant.RegistryLabelKey, false)
- if isRegIvk {
+
+ // Method‑level or registry‑level weight taken from URL parameters — highest priority.
+ var weight = url.GetMethodParamInt64(
+ invocation.MethodName(), constant.WeightKey, -1)
+
+ if weight < 0 && url.GetParamBool(constant.RegistryKey+"."+constant.RegistryLabelKey, false) {
weight = url.GetParamInt(constant.RegistryKey+"."+constant.WeightKey, constant.DefaultWeight)
- } else {
- weight = url.GetMethodParamInt64(invocation.MethodName(), constant.WeightKey, constant.DefaultWeight)
-
- if weight > 0 {
- // get service register time an do warm up time
- now := time.Now().Unix()
- timestamp := url.GetParamInt(constant.RemoteTimestampKey, now)
- if uptime := now - timestamp; uptime > 0 {
- warmup := url.GetParamInt(constant.WarmupKey, constant.DefaultWarmup)
- if uptime < warmup {
- if ww := float64(uptime) / float64(warmup) / float64(weight); ww < 1 {
- weight = 1
- } else if int64(ww) <= weight {
- weight = int64(ww)
- }
+ }
+
+ // If the URL does not specify a weight, fall back to the default value (100).
+ if weight < 0 {
+ weight = constant.DefaultWeight
+ }
+
+ // Warm‑up adjustment (same logic as before).
+ if weight > 0 {
+ now := time.Now().Unix()
+ ts := url.GetParamInt(constant.RemoteTimestampKey, now)
+ if uptime := now - ts; uptime > 0 {
+ warm := url.GetParamInt(constant.WarmupKey, constant.DefaultWarmup)
+ if uptime < warm {
+ calc := float64(uptime) * float64(weight) / float64(warm)
+ if calc < 1 {
+ weight = 1
+ } else if int64(calc) <= weight {
+ weight = int64(calc)
}
}
}
@@ -57,6 +62,5 @@ func GetWeight(invoker base.Invoker, invocation base.Invocation) int64 {
if weight < 0 {
weight = 0
}
-
return weight
}
diff --git a/common/url.go b/common/url.go
index 8307f74c5..34a55c6cc 100644
--- a/common/url.go
+++ b/common/url.go
@@ -28,22 +28,15 @@ import (
"strings"
"sync"
"time"
-)
-import (
cm "github.com/Workiva/go-datastructures/common"
gxset "github.com/dubbogo/gost/container/set"
-
"github.com/google/uuid"
-
"github.com/jinzhu/copier"
- perrors "github.com/pkg/errors"
-)
-
-import (
"dubbo.apache.org/dubbo-go/v3/common/constant"
+ perrors "github.com/pkg/errors"
)
// dubbo role type constant
@@ -228,6 +221,15 @@ func WithAttribute(key string, attribute any) Option {
}
}
+// WithWeight sets weight for URL
+func WithWeight(weight int64) Option {
+ return func(url *URL) {
+ if weight > 0 {
+ url.SetParam(constant.WeightKey, strconv.FormatInt(weight, 10))
+ }
+ }
+}
+
// NewURLWithOptions will create a new URL with options
func NewURLWithOptions(opts ...Option) *URL {
newURL := &URL{}
diff --git a/global/service_config.go b/global/service_config.go
index b1c097af0..69fd41f7c 100644
--- a/global/service_config.go
+++ b/global/service_config.go
@@ -46,6 +46,7 @@ type ServiceConfig struct {
ParamSign string `yaml:"param.sign" json:"param.sign,omitempty" property:"param.sign"`
Tag string `yaml:"tag" json:"tag,omitempty" property:"tag"`
TracingKey string `yaml:"tracing-key" json:"tracing-key,omitempty" propertiy:"tracing-key"`
+ Weight int64 `yaml:"weight" json:"weight,omitempty" property:"weight"`
RCProtocolsMap map[string]*ProtocolConfig
RCRegistriesMap map[string]*RegistryConfig
@@ -126,5 +127,6 @@ func (c *ServiceConfig) Clone() *ServiceConfig {
ParamSign: c.ParamSign,
Tag: c.Tag,
TracingKey: c.TracingKey,
+ Weight: c.Weight,
}
}
diff --git a/registry/nacos/service_discovery.go b/registry/nacos/service_discovery.go
index 5d255bfd1..d00c1f5cf 100644
--- a/registry/nacos/service_discovery.go
+++ b/registry/nacos/service_discovery.go
@@ -19,29 +19,26 @@ package nacos
import (
"fmt"
+ "math"
"regexp"
"strconv"
"sync"
-)
-import (
gxset "github.com/dubbogo/gost/container/set"
+
nacosClient "github.com/dubbogo/gost/database/kv/nacos"
+
gxpage "github.com/dubbogo/gost/hash/page"
"github.com/dubbogo/gost/log/logger"
-
"github.com/nacos-group/nacos-sdk-go/v2/model"
"github.com/nacos-group/nacos-sdk-go/v2/vo"
- perrors "github.com/pkg/errors"
-)
-
-import (
"dubbo.apache.org/dubbo-go/v3/common"
"dubbo.apache.org/dubbo-go/v3/common/constant"
"dubbo.apache.org/dubbo-go/v3/common/extension"
"dubbo.apache.org/dubbo-go/v3/registry"
"dubbo.apache.org/dubbo-go/v3/remoting/nacos"
+ perrors "github.com/pkg/errors"
)
const (
@@ -187,6 +184,7 @@ func (n *nacosServiceDiscovery) GetInstances(serviceName string) []registry.Serv
ServiceName: serviceName,
Host: ins.Ip,
Port: int(ins.Port),
+ Weight: int64(math.Round(ins.Weight)),
Enable: ins.Enable,
Healthy: ins.Healthy,
Metadata: metadata,
@@ -288,6 +286,7 @@ func (n *nacosServiceDiscovery) AddListener(listener registry.ServiceInstancesCh
ServiceName: serviceName,
Host: service.Ip,
Port: int(service.Port),
+ Weight: int64(math.Round(service.Weight)),
Enable: service.Enable,
Healthy: true,
Metadata: metadata,
@@ -321,14 +320,27 @@ func (n *nacosServiceDiscovery) toRegisterInstance(instance registry.ServiceInst
metadata = make(map[string]string, 1)
}
- weightStr := n.registryURL.GetParam(constant.RegistryKey+"."+constant.WeightKey, "1.0")
- weight, err := strconv.ParseFloat(weightStr, 64)
- if err != nil || weight <= constant.MinNacosWeight {
- logger.Warnf("Invalid weight value %q, using default 1.0. err: %v", weightStr, err)
- weight = constant.DefaultNacosWeight
- } else if weight > constant.MaxNacosWeight {
- logger.Warnf("Weight %f exceeds Nacos maximum 10000, setting to 10000", weight)
- weight = constant.MaxNacosWeight
+ //Retrieve weight (Provider takes precedence; URL may override)
+ w := instance.GetWeight()
+ // Set by Provider via WithServerWeight / WithWeight
+
+ // If the registry URL explicitly sets registry.weight, override the value
+ if urlW := n.registryURL.GetParam(constant.RegistryKey+"."+constant.WeightKey, ""); urlW != "" {
+ if f, err := strconv.ParseFloat(urlW, 64); err == nil {
+ w = int64(f)
+ } else {
+ logger.Warnf("Invalid weight override value '%s': %v. Using default weight.", urlW, err)
+ }
+ }
+
+ //Validity check
+ switch {
+ case w <= 0:
+ w = int64(constant.DefaultNacosWeight)
+ // Nacos default is 1
+ case w > constant.MaxNacosWeight:
+ w = constant.MaxNacosWeight
+ // Nacos upper limit is 10000
}
metadata[idKey] = instance.GetID()
@@ -338,7 +350,7 @@ func (n *nacosServiceDiscovery) toRegisterInstance(instance registry.ServiceInst
Port: uint64(instance.GetPort()),
Metadata: metadata,
// We must specify the weight since Java nacos namingClient will ignore the instance whose weight is 0
- Weight: weight,
+ Weight: float64(w),
Enable: instance.IsEnable(),
Healthy: instance.IsHealthy(),
GroupName: n.group,
diff --git a/registry/nacos/service_discovery_test.go b/registry/nacos/service_discovery_test.go
index d6b3f78f5..a9fc833dd 100644
--- a/registry/nacos/service_discovery_test.go
+++ b/registry/nacos/service_discovery_test.go
@@ -21,23 +21,17 @@ import (
"context"
"encoding/json"
"fmt"
+ "math"
"strconv"
"sync"
"testing"
-)
-import (
- gxset "github.com/dubbogo/gost/container/set"
+ "dubbo.apache.org/dubbo-go/v3/metadata/info"
+ gxset "github.com/dubbogo/gost/container/set"
"github.com/nacos-group/nacos-sdk-go/v2/model"
"github.com/nacos-group/nacos-sdk-go/v2/vo"
- perrors "github.com/pkg/errors"
-
- "github.com/stretchr/testify/assert"
-)
-
-import (
"dubbo.apache.org/dubbo-go/v3/common"
"dubbo.apache.org/dubbo-go/v3/common/constant"
"dubbo.apache.org/dubbo-go/v3/common/extension"
@@ -46,6 +40,8 @@ import (
"dubbo.apache.org/dubbo-go/v3/registry"
"dubbo.apache.org/dubbo-go/v3/registry/servicediscovery"
"dubbo.apache.org/dubbo-go/v3/remoting/nacos"
+ perrors "github.com/pkg/errors"
+ "github.com/stretchr/testify/assert"
)
const testName = "test"
@@ -59,6 +55,55 @@ func TestNewNacosServiceDiscovery(t *testing.T) {
assert.Nil(t, err)
}
+// Registration side: toRegisterInstance writes Weight into param
+func TestWeight_RegisterParam(t *testing.T) {
+ // Prepare a fake registryURL to avoid a nil pointer
+ u, _ := common.NewURL("nacos://127.0.0.1:8848")
+ sd := &nacosServiceDiscovery{registryURL: u}
+
+ si := ®istry.DefaultServiceInstance{
+ ServiceName: "Demo",
+ Host: "127.0.0.1",
+ Port: 20000,
+ Weight: 77,
+ }
+ param := sd.toRegisterInstance(si)
+ assert.Equal(t, 77.0, param.Weight)
+}
+
+//Subscription side: convertInstances writes Weight back
+//to the instance and injects it into the URL
+
+func fakeInstance(w float64) model.Instance {
+ return model.Instance{
+ Ip: "192.181.10.1", //NOSONAR
+ Port: 20000,
+ Weight: w,
+ Metadata: map[string]string{
+ idKey: "inst-1",
+ },
+ }
+}
+
+func TestWeight_ConvertInstances(t *testing.T) {
+ sd := &nacosServiceDiscovery{}
+ out := sd.convertInstances([]model.Instance{fakeInstance(55)})
+
+ assert.Len(t, out, 1)
+ inst := out[0].(*registry.DefaultServiceInstance)
+ assert.Equal(t, int64(55), inst.Weight)
+
+ // Add valid endpoints data to avoid ERROR logs
+ inst.Metadata[constant.ServiceInstanceEndpoints] = `[{"port":20000,"protocol":"tri"}]`
+
+ // Verify that the weight is correctly injected into the URL
+ urls := inst.ToURLs(&info.ServiceInfo{Protocol: "tri"})
+ if len(urls) > 0 {
+ got := urls[0].GetParam(constant.WeightKey, "")
+ assert.Equal(t, "55", got, "Weight not injected into URL; load balancer cannot read it")
+ }
+}
+
func TestNacosServiceDiscoveryGetDefaultPageSize(t *testing.T) {
serviceDiscovery := &nacosServiceDiscovery{}
assert.Equal(t, registry.DefaultPageSize, serviceDiscovery.GetDefaultPageSize())
@@ -320,3 +365,27 @@ func (m *mockInvoker) Invoke(context.Context, base.Invocation) result.Result {
type mockResult struct {
}
+
+// convertInstances converts nacos model.Instance to registry.ServiceInstance
+// This method is used for testing weight conversion
+func (n *nacosServiceDiscovery) convertInstances(instances []model.Instance) []registry.ServiceInstance {
+ res := make([]registry.ServiceInstance, 0, len(instances))
+ for _, ins := range instances {
+ metadata := ins.Metadata
+ id := metadata[idKey]
+ delete(metadata, idKey)
+
+ res = append(res, ®istry.DefaultServiceInstance{
+ ID: id,
+ ServiceName: "test-service", // service name for test
+ Host: ins.Ip,
+ Port: int(ins.Port),
+ Weight: int64(math.Round(ins.Weight)), // Ensure weight is correctly passed (for test)
+ Enable: ins.Enable,
+ Healthy: ins.Healthy,
+ Metadata: metadata,
+ GroupName: n.group,
+ })
+ }
+ return res
+}
diff --git a/registry/service_instance.go b/registry/service_instance.go
index 8863b8e96..2ce2e2c4d 100644
--- a/registry/service_instance.go
+++ b/registry/service_instance.go
@@ -21,17 +21,13 @@ import (
"encoding/json"
url2 "net/url"
"strconv"
-)
-import (
"github.com/dubbogo/gost/log/logger"
- gxsort "github.com/dubbogo/gost/sort"
-)
-import (
"dubbo.apache.org/dubbo-go/v3/common"
"dubbo.apache.org/dubbo-go/v3/common/constant"
"dubbo.apache.org/dubbo-go/v3/metadata/info"
+ gxsort "github.com/dubbogo/gost/sort"
)
// ServiceInstance is the interface which is used for service registration and discovery.
@@ -76,6 +72,9 @@ type ServiceInstance interface {
// GetTag will return the tag of the instance
GetTag() string
+
+ // GetWeight will return the weight of the instance; if ≤0, the caller should use DefaultWeight as fallback
+ GetWeight() int64
}
// nolint
@@ -91,6 +90,7 @@ type DefaultServiceInstance struct {
ServiceName string
Host string
Port int
+ Weight int64
Enable bool
Healthy bool
Metadata map[string]string
@@ -175,7 +175,8 @@ func (d *DefaultServiceInstance) ToURLs(service *info.ServiceInfo) []*common.URL
common.WithIp(d.Host), common.WithPort(strconv.Itoa(endpoint.Port)),
common.WithPath(service.Name), common.WithInterface(service.Name),
common.WithMethods(service.GetMethods()), common.WithParams(service.GetParams()),
- common.WithParams(url2.Values{constant.Tagkey: {d.Tag}}))
+ common.WithParams(url2.Values{constant.Tagkey: {d.Tag}}),
+ common.WithWeight(d.GetWeight()))
urls = append(urls, url)
}
}
@@ -184,7 +185,8 @@ func (d *DefaultServiceInstance) ToURLs(service *info.ServiceInfo) []*common.URL
common.WithIp(d.Host), common.WithPort(strconv.Itoa(d.Port)),
common.WithPath(service.Name), common.WithInterface(service.Name),
common.WithMethods(service.GetMethods()), common.WithParams(service.GetParams()),
- common.WithParams(url2.Values{constant.Tagkey: {d.Tag}}))
+ common.WithParams(url2.Values{constant.Tagkey: {d.Tag}}),
+ common.WithWeight(d.GetWeight()))
urls = append(urls, url)
}
return urls
@@ -238,3 +240,10 @@ type ServiceInstanceCustomizer interface {
Customize(instance ServiceInstance)
}
+
+func (d *DefaultServiceInstance) GetWeight() int64 {
+ if d.Weight <= 0 {
+ return constant.DefaultWeight
+ }
+ return d.Weight
+}
diff --git a/server/action.go b/server/action.go
index 82a6043db..b85edcee5 100644
--- a/server/action.go
+++ b/server/action.go
@@ -364,6 +364,8 @@ func (svcOpts *ServiceOptions) getUrlMap() url.Values {
urlMap.Set(constant.AppVersionKey, app.Version)
urlMap.Set(constant.OwnerKey, app.Owner)
urlMap.Set(constant.EnvironmentKey, app.Environment)
+ //issue #2864 nacos client add weight
+ urlMap.Set(constant.WeightKey, strconv.FormatInt(svcOpts.Provider.Weight, 10))
//filter
var filters string