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 ba176139c refactor(client): remove config compat layer, use global 
types directly (#3232)
ba176139c is described below

commit ba176139c13a31ae31466ec15c38b4a734137e1b
Author: nanjiek <[email protected]>
AuthorDate: Mon Mar 16 13:04:56 2026 +0800

    refactor(client): remove config compat layer, use global types directly 
(#3232)
    
    * remove the config package from package client
    
    * fix(client): fail fast on invalid registry and method config
    
    * restore WithMethod in Server
    
    * fix lint of action_test
    
    * refactor: split processURL to reduce cognitive complexity
---
 client/action.go        | 113 +++++++++++++++++++++++++++---------------
 client/action_test.go   | 129 ++++++++++++++++++++++++++++++++++++++++++++++++
 client/client.go        |  28 +++++++++--
 client/client_test.go   |  25 ++++++++++
 client/compat.go        |  81 ------------------------------
 client/options.go       | 106 ++++++++++++---------------------------
 client/options_test.go  |  96 +++++++++++++++++++++++++++++------
 global/config_test.go   |   1 +
 internal/config.go      |  55 +++++++++++++++++++--
 internal/config_test.go |  60 ++++++++++++++++++++--
 server/action.go        |   6 ++-
 server/action_test.go   |  21 ++++++++
 server/options.go       |  33 +++++++++++++
 server/options_test.go  |  81 ++++++++++++++++++++++++++++++
 14 files changed, 609 insertions(+), 226 deletions(-)

diff --git a/client/action.go b/client/action.go
index 75e95a6c1..2b9aca347 100644
--- a/client/action.go
+++ b/client/action.go
@@ -40,9 +40,9 @@ import (
        commonCfg "dubbo.apache.org/dubbo-go/v3/common/config"
        "dubbo.apache.org/dubbo-go/v3/common/constant"
        "dubbo.apache.org/dubbo-go/v3/common/extension"
-       "dubbo.apache.org/dubbo-go/v3/config"
        "dubbo.apache.org/dubbo-go/v3/global"
        "dubbo.apache.org/dubbo-go/v3/graceful_shutdown"
+       "dubbo.apache.org/dubbo-go/v3/internal"
        "dubbo.apache.org/dubbo-go/v3/protocol/base"
        "dubbo.apache.org/dubbo-go/v3/protocol/protocolwrapper"
        "dubbo.apache.org/dubbo-go/v3/proxy"
@@ -178,7 +178,7 @@ func (refOpts *ReferenceOptions) refer(srv 
common.RPCService, info *ClientInfo)
        updateOrCreateMeshURL(refOpts)
 
        // retrieving urls from config, and appending the urls to refOpts.urls
-       urls, err := processURL(ref, refOpts.registriesCompat, cfgURL)
+       urls, err := processURL(ref, refOpts.Registries, cfgURL)
        if err != nil {
                panic(err)
        }
@@ -209,51 +209,82 @@ func (refOpts *ReferenceOptions) refer(srv 
common.RPCService, info *ClientInfo)
        graceful_shutdown.RegisterProtocol(ref.Protocol)
 }
 
-func processURL(ref *global.ReferenceConfig, regsCompat 
map[string]*config.RegistryConfig, cfgURL *common.URL) ([]*common.URL, error) {
+func processURL(ref *global.ReferenceConfig, registries 
map[string]*global.RegistryConfig, cfgURL *common.URL) ([]*common.URL, error) {
+       if ref.URL != "" {
+               return processUserSpecifiedURLs(ref, cfgURL)
+       }
+
+       return loadRegistryURLs(ref, registries, cfgURL)
+}
+
+func processUserSpecifiedURLs(ref *global.ReferenceConfig, cfgURL *common.URL) 
([]*common.URL, error) {
+       /*
+                Two types of URL are allowed for refOpts.URL:
+                       1. direct url: server IP, that is, no need for a 
registry anymore
+                       2. registry url
+                They will be handled in different ways:
+                For example, we have a direct url and a registry url:
+                       1. "tri://localhost:10000" is a direct url
+                       2. "registry://localhost:2181" is a registry url.
+                Then, refOpts.URL looks like a string separated by semicolon: 
"tri://localhost:10000;registry://localhost:2181".
+                The result of urlStrings is a string array: 
[]string{"tri://localhost:10000", "registry://localhost:2181"}.
+       */
        var urls []*common.URL
-       if ref.URL != "" { // use user-specific urls
-               /*
-                        Two types of URL are allowed for refOpts.URL:
-                               1. direct url: server IP, that is, no need for 
a registry anymore
-                               2. registry url
-                        They will be handled in different ways:
-                        For example, we have a direct url and a registry url:
-                               1. "tri://localhost:10000" is a direct url
-                               2. "registry://localhost:2181" is a registry 
url.
-                        Then, refOpts.URL looks like a string separated by 
semicolon: "tri://localhost:10000;registry://localhost:2181".
-                        The result of urlStrings is a string array: 
[]string{"tri://localhost:10000", "registry://localhost:2181"}.
-               */
-               urlStrings := gxstrings.RegSplit(ref.URL, "\\s*[;]+\\s*")
-               for _, urlStr := range urlStrings {
-                       serviceURL, err := common.NewURL(urlStr, 
common.WithProtocol(ref.Protocol))
-                       if err != nil {
-                               return nil, fmt.Errorf("url configuration 
error,  please check your configuration, user specified URL %v refer error, 
error message is %v ", urlStr, err.Error())
-                       }
-                       if serviceURL.Protocol == constant.RegistryProtocol { 
// serviceURL in this branch is a registry protocol
-                               serviceURL.SubURL = cfgURL
-                               urls = append(urls, serviceURL)
-                       } else { // serviceURL in this branch is the target 
endpoint IP address
-                               if serviceURL.Path == "" {
-                                       serviceURL.Path = "/" + 
ref.InterfaceName
-                               }
-                               // replace params of serviceURL with params of 
cfgUrl
-                               // other stuff, e.g. IP, port, etc., are same 
as serviceURL
-                               newURL := serviceURL.MergeURL(cfgURL)
-                               newURL.AddParam("peer", "true")
-                               urls = append(urls, newURL)
-                       }
-               }
-       } else { // use registry configs
-               urls = config.LoadRegistries(ref.RegistryIDs, regsCompat, 
common.CONSUMER)
-               // set url to regURLs
-               for _, regURL := range urls {
-                       regURL.SubURL = cfgURL
+       urlStrings := gxstrings.RegSplit(ref.URL, "\\s*[;]+\\s*")
+
+       for _, urlStr := range urlStrings {
+               serviceURL, err := common.NewURL(urlStr, 
common.WithProtocol(ref.Protocol))
+               if err != nil {
+                       return nil, fmt.Errorf("url configuration error,  
please check your configuration, user specified URL %v refer error, error 
message is %v ", urlStr, err.Error())
                }
+
+               urls = append(urls, buildReferenceURL(serviceURL, ref, cfgURL))
        }
+
        return urls, nil
 }
 
+func buildReferenceURL(serviceURL *common.URL, ref *global.ReferenceConfig, 
cfgURL *common.URL) *common.URL {
+       if serviceURL.Protocol == constant.RegistryProtocol {
+               serviceURL.SubURL = cfgURL
+               return serviceURL
+       }
+
+       if serviceURL.Path == "" {
+               serviceURL.Path = "/" + ref.InterfaceName
+       }
+
+       // replace params of serviceURL with params of cfgUrl
+       // other stuff, e.g. IP, port, etc., are same as serviceURL
+       newURL := serviceURL.MergeURL(cfgURL)
+       newURL.AddParam("peer", "true")
+       return newURL
+}
+
+func loadRegistryURLs(ref *global.ReferenceConfig, registries 
map[string]*global.RegistryConfig, cfgURL *common.URL) ([]*common.URL, error) {
+       urls, err := internal.LoadRegistries(ref.RegistryIDs, registries, 
common.CONSUMER)
+       if err != nil {
+               return nil, err
+       }
+       if len(urls) == 0 {
+               return nil, fmt.Errorf("no registry urls available for registry 
ids %v", ref.RegistryIDs)
+       }
+
+       attachSubURL(urls, cfgURL)
+       return urls, nil
+}
+
+func attachSubURL(urls []*common.URL, cfgURL *common.URL) {
+       for _, regURL := range urls {
+               regURL.SubURL = cfgURL
+       }
+}
+
 func buildInvoker(urls []*common.URL, ref *global.ReferenceConfig) 
(base.Invoker, error) {
+       if len(urls) == 0 {
+               return nil, fmt.Errorf("no urls available to build invoker")
+       }
+
        var (
                invoker base.Invoker
                regURL  *common.URL
@@ -355,7 +386,7 @@ func (refOpts *ReferenceOptions) GetProxy() *proxy.Proxy {
 
 func (refOpts *ReferenceOptions) getURLMap() url.Values {
        ref := refOpts.Reference
-       app := refOpts.applicationCompat
+       app := refOpts.Application
        metrics := refOpts.Metrics
        tracing := refOpts.Otel.TracingConfig
 
diff --git a/client/action_test.go b/client/action_test.go
index bb2493c88..fb2834c75 100644
--- a/client/action_test.go
+++ b/client/action_test.go
@@ -28,6 +28,7 @@ import (
 )
 
 import (
+       "dubbo.apache.org/dubbo-go/v3/common"
        "dubbo.apache.org/dubbo-go/v3/common/constant"
        "dubbo.apache.org/dubbo-go/v3/global"
 )
@@ -97,3 +98,131 @@ func TestUpdateOrCreateMeshURL(t *testing.T) {
                })
        })
 }
+
+func TestProcessURLPropagatesRegistryError(t *testing.T) {
+       ref := &global.ReferenceConfig{
+               InterfaceName: "com.example.Service",
+               RegistryIDs:   []string{"bad"},
+       }
+       cfgURL := common.NewURLWithOptions(common.WithPath(ref.InterfaceName))
+       registries := map[string]*global.RegistryConfig{
+               "bad": {
+                       Protocol: "mock",
+                       Address:  "127.0.0.1:bad",
+               },
+       }
+
+       urls, err := processURL(ref, registries, cfgURL)
+       require.Nil(t, urls)
+       require.Error(t, err)
+       require.Contains(t, err.Error(), `registry id "bad" url is invalid`)
+}
+
+func TestProcessURLRejectsEmptyRegistryURLs(t *testing.T) {
+       ref := &global.ReferenceConfig{
+               InterfaceName: "com.example.Service",
+               RegistryIDs:   []string{"empty"},
+       }
+       cfgURL := common.NewURLWithOptions(common.WithPath(ref.InterfaceName))
+       registries := map[string]*global.RegistryConfig{
+               "empty": {
+                       Protocol: "mock",
+                       Address:  constant.NotAvailable,
+               },
+       }
+
+       urls, err := processURL(ref, registries, cfgURL)
+       require.Nil(t, urls)
+       require.Error(t, err)
+       require.Contains(t, err.Error(), "no registry urls available")
+}
+
+func TestProcessURLWithDirectUserURL(t *testing.T) {
+       ref := &global.ReferenceConfig{
+               InterfaceName: "com.example.Service",
+               Protocol:      constant.TriProtocol,
+               URL:           "tri://localhost:20000",
+       }
+       cfgURL := common.NewURLWithOptions(
+               common.WithPath(ref.InterfaceName),
+               common.WithParamsValue(constant.GroupKey, "test-group"),
+       )
+
+       urls, err := processURL(ref, nil, cfgURL)
+       require.NoError(t, err)
+       require.Len(t, urls, 1)
+       require.Equal(t, constant.TriProtocol, urls[0].Protocol)
+       require.Equal(t, "/"+ref.InterfaceName, urls[0].Path)
+       require.Equal(t, "test-group", urls[0].GetParam(constant.GroupKey, ""))
+       require.Equal(t, "true", urls[0].GetParam("peer", ""))
+}
+
+func TestProcessURLWithMultipleDirectUserURLs(t *testing.T) {
+       ref := &global.ReferenceConfig{
+               InterfaceName: "com.example.Service",
+               Protocol:      constant.TriProtocol,
+               URL:           "tri://localhost:20000;tri://localhost:20001",
+       }
+       cfgURL := common.NewURLWithOptions(common.WithPath(ref.InterfaceName))
+
+       urls, err := processURL(ref, nil, cfgURL)
+       require.NoError(t, err)
+       require.Len(t, urls, 2)
+       require.Equal(t, constant.TriProtocol, urls[0].Protocol)
+       require.Equal(t, "true", urls[0].GetParam("peer", ""))
+       require.Equal(t, constant.TriProtocol, urls[1].Protocol)
+       require.Equal(t, "true", urls[1].GetParam("peer", ""))
+}
+
+func TestProcessURLRejectsInvalidUserURL(t *testing.T) {
+       ref := &global.ReferenceConfig{
+               InterfaceName: "com.example.Service",
+               Protocol:      constant.TriProtocol,
+               URL:           "://bad-url",
+       }
+       cfgURL := common.NewURLWithOptions(common.WithPath(ref.InterfaceName))
+
+       urls, err := processURL(ref, nil, cfgURL)
+       require.Nil(t, urls)
+       require.Error(t, err)
+       require.Contains(t, err.Error(), "url configuration error")
+}
+
+func TestBuildReferenceURLWithRegistryProtocol(t *testing.T) {
+       ref := &global.ReferenceConfig{InterfaceName: "com.example.Service"}
+       cfgURL := common.NewURLWithOptions(common.WithPath(ref.InterfaceName))
+       serviceURL := 
common.NewURLWithOptions(common.WithProtocol(constant.RegistryProtocol))
+
+       result := buildReferenceURL(serviceURL, ref, cfgURL)
+
+       require.Same(t, serviceURL, result)
+       require.Same(t, cfgURL, result.SubURL)
+}
+
+func TestLoadRegistryURLsAssignsSubURL(t *testing.T) {
+       ref := &global.ReferenceConfig{
+               InterfaceName: "com.example.Service",
+               RegistryIDs:   []string{"valid"},
+       }
+       cfgURL := common.NewURLWithOptions(common.WithPath(ref.InterfaceName))
+       registries := map[string]*global.RegistryConfig{
+               "valid": {
+                       Protocol: "zookeeper",
+                       Address:  "127.0.0.1:2181",
+               },
+       }
+
+       urls, err := loadRegistryURLs(ref, registries, cfgURL)
+       require.NoError(t, err)
+       require.NotEmpty(t, urls)
+       for _, regURL := range urls {
+               require.Same(t, cfgURL, regURL.SubURL)
+       }
+}
+
+func TestBuildInvokerRejectsEmptyURLs(t *testing.T) {
+       invoker, err := buildInvoker(nil, &global.ReferenceConfig{})
+       require.Nil(t, invoker)
+       require.Error(t, err)
+       require.Contains(t, err.Error(), "no urls available")
+}
diff --git a/client/client.go b/client/client.go
index e31fdc7f2..e443e5da6 100644
--- a/client/client.go
+++ b/client/client.go
@@ -27,6 +27,7 @@ import (
        "dubbo.apache.org/dubbo-go/v3/common"
        "dubbo.apache.org/dubbo-go/v3/common/constant"
        "dubbo.apache.org/dubbo-go/v3/filter/generic"
+       "dubbo.apache.org/dubbo-go/v3/global"
        "dubbo.apache.org/dubbo-go/v3/metadata"
        "dubbo.apache.org/dubbo-go/v3/protocol/base"
        "dubbo.apache.org/dubbo-go/v3/protocol/invocation"
@@ -187,15 +188,10 @@ func (cli *Client) DialWithDefinition(interfaceName 
string, definition *ClientDe
 }
 
 func (cli *Client) dial(interfaceName string, info *ClientInfo, srv any, opts 
...ReferenceOption) (*Connection, error) {
-       if err := metadata.InitRegistryMetadataReport(cli.cliOpts.Registries); 
err != nil {
-               return nil, err
-       }
        newRefOpts := defaultReferenceOptions()
        finalOpts := []ReferenceOption{
                setReference(cli.cliOpts.overallReference),
                setApplication(cli.cliOpts.Application),
-               setApplicationCompat(cli.cliOpts.applicationCompat),
-               setRegistriesCompat(cli.cliOpts.registriesCompat),
                setRegistries(cli.cliOpts.Registries),
                setConsumer(cli.cliOpts.Consumer),
                setShutdown(cli.cliOpts.Shutdown),
@@ -211,6 +207,11 @@ func (cli *Client) dial(interfaceName string, info 
*ClientInfo, srv any, opts ..
        if err := newRefOpts.init(finalOpts...); err != nil {
                return nil, err
        }
+       effectiveRegistries := 
filterRegistriesByIDs(newRefOpts.Reference.RegistryIDs, newRefOpts.Registries)
+       if err := metadata.InitRegistryMetadataReport(effectiveRegistries); err 
!= nil {
+               return nil, err
+       }
+       newRefOpts.Registries = effectiveRegistries
 
        if info != nil {
                newRefOpts.ReferWithInfo(info)
@@ -223,6 +224,23 @@ func (cli *Client) dial(interfaceName string, info 
*ClientInfo, srv any, opts ..
        return &Connection{refOpts: newRefOpts}, nil
 }
 
+func filterRegistriesByIDs(ids []string, regs 
map[string]*global.RegistryConfig) map[string]*global.RegistryConfig {
+       if len(regs) == 0 {
+               return regs
+       }
+       if len(ids) == 0 || (len(ids) == 1 && ids[0] == "") {
+               return regs
+       }
+
+       filtered := make(map[string]*global.RegistryConfig, len(ids))
+       for _, id := range ids {
+               if reg, ok := regs[id]; ok {
+                       filtered[id] = reg
+               }
+       }
+       return filtered
+}
+
 func generateInvocation(ctx context.Context, methodName string, reqs []any, 
resp any, callType string, opts *CallOptions) (base.Invocation, error) {
        var paramsRawVals []any
 
diff --git a/client/client_test.go b/client/client_test.go
index 4f9766f16..95d2e5ea1 100644
--- a/client/client_test.go
+++ b/client/client_test.go
@@ -31,6 +31,7 @@ import (
 import (
        "dubbo.apache.org/dubbo-go/v3/common"
        "dubbo.apache.org/dubbo-go/v3/common/constant"
+       "dubbo.apache.org/dubbo-go/v3/global"
        "dubbo.apache.org/dubbo-go/v3/protocol/base"
        "dubbo.apache.org/dubbo-go/v3/protocol/result"
 )
@@ -241,3 +242,27 @@ func requireCallType(t *testing.T, inv base.Invocation, 
callType string) {
        require.True(t, ok)
        require.Equal(t, callType, attr)
 }
+
+func TestFilterRegistriesByIDs(t *testing.T) {
+       regs := map[string]*global.RegistryConfig{
+               "r1": {Protocol: "mock", Address: "127.0.0.1:2181"},
+               "r2": {Protocol: "mock", Address: "127.0.0.2:2181"},
+       }
+
+       filtered := filterRegistriesByIDs([]string{"r2"}, regs)
+       require.Len(t, filtered, 1)
+       require.Contains(t, filtered, "r2")
+       require.NotContains(t, filtered, "r1")
+}
+
+func TestFilterRegistriesByIDsReturnsAllWhenIDsEmpty(t *testing.T) {
+       regs := map[string]*global.RegistryConfig{
+               "r1": {Protocol: "mock", Address: "127.0.0.1:2181"},
+               "r2": {Protocol: "mock", Address: "127.0.0.2:2181"},
+       }
+
+       filtered := filterRegistriesByIDs(nil, regs)
+       require.Len(t, filtered, 2)
+       require.Contains(t, filtered, "r1")
+       require.Contains(t, filtered, "r2")
+}
diff --git a/client/compat.go b/client/compat.go
deleted file mode 100644
index 3a91c9725..000000000
--- a/client/compat.go
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * 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 client
-
-import (
-       "dubbo.apache.org/dubbo-go/v3/config"
-       "dubbo.apache.org/dubbo-go/v3/global"
-)
-
-// these functions are used to resolve circular dependencies temporarily.
-// please refer to issue(https://github.com/apache/dubbo-go/issues/2377)
-// todo(DMwangnima): remove these functions when refactoring dubbo-go
-func compatApplicationConfig(c *global.ApplicationConfig) 
*config.ApplicationConfig {
-       return &config.ApplicationConfig{
-               Organization:            c.Organization,
-               Name:                    c.Name,
-               Module:                  c.Module,
-               Group:                   c.Group,
-               Version:                 c.Version,
-               Owner:                   c.Owner,
-               Environment:             c.Environment,
-               MetadataType:            c.MetadataType,
-               Tag:                     c.Tag,
-               MetadataServicePort:     c.MetadataServicePort,
-               MetadataServiceProtocol: c.MetadataServiceProtocol,
-       }
-}
-
-func compatRegistryConfig(c *global.RegistryConfig) *config.RegistryConfig {
-       return &config.RegistryConfig{
-               Protocol:          c.Protocol,
-               Timeout:           c.Timeout,
-               Group:             c.Group,
-               Namespace:         c.Namespace,
-               TTL:               c.TTL,
-               Address:           c.Address,
-               Username:          c.Username,
-               Password:          c.Password,
-               Simplified:        c.Simplified,
-               Preferred:         c.Preferred,
-               Zone:              c.Zone,
-               Weight:            c.Weight,
-               Params:            c.Params,
-               RegistryType:      c.RegistryType,
-               UseAsMetaReport:   c.UseAsMetaReport,
-               UseAsConfigCenter: c.UseAsConfigCenter,
-       }
-}
-
-func compatMethodConfig(c *global.MethodConfig) *config.MethodConfig {
-       return &config.MethodConfig{
-               InterfaceId:                 c.InterfaceId,
-               InterfaceName:               c.InterfaceName,
-               Name:                        c.Name,
-               Retries:                     c.Retries,
-               LoadBalance:                 c.LoadBalance,
-               Weight:                      c.Weight,
-               TpsLimitInterval:            c.TpsLimitInterval,
-               TpsLimitRate:                c.TpsLimitRate,
-               TpsLimitStrategy:            c.TpsLimitStrategy,
-               ExecuteLimit:                c.ExecuteLimit,
-               ExecuteLimitRejectedHandler: c.ExecuteLimitRejectedHandler,
-               Sticky:                      c.Sticky,
-               RequestTimeout:              c.RequestTimeout,
-       }
-}
diff --git a/client/options.go b/client/options.go
index f837b1501..e2d6f9b3f 100644
--- a/client/options.go
+++ b/client/options.go
@@ -18,6 +18,7 @@
 package client
 
 import (
+       "fmt"
        "strconv"
        "time"
 )
@@ -30,9 +31,9 @@ import (
        "dubbo.apache.org/dubbo-go/v3/common"
        commonCfg "dubbo.apache.org/dubbo-go/v3/common/config"
        "dubbo.apache.org/dubbo-go/v3/common/constant"
-       "dubbo.apache.org/dubbo-go/v3/config"
        "dubbo.apache.org/dubbo-go/v3/global"
        "dubbo.apache.org/dubbo-go/v3/graceful_shutdown"
+       "dubbo.apache.org/dubbo-go/v3/internal"
        "dubbo.apache.org/dubbo-go/v3/protocol"
        "dubbo.apache.org/dubbo-go/v3/protocol/base"
        "dubbo.apache.org/dubbo-go/v3/proxy"
@@ -57,10 +58,6 @@ type ReferenceOptions struct {
        urls         []*common.URL
        metaDataType string
        info         *ClientInfo
-
-       methodsCompat     []*config.MethodConfig
-       applicationCompat *config.ApplicationConfig
-       registriesCompat  map[string]*config.RegistryConfig
 }
 
 func defaultReferenceOptions() *ReferenceOptions {
@@ -86,7 +83,7 @@ func (refOpts *ReferenceOptions) init(opts 
...ReferenceOption) error {
 
        refConf := refOpts.Reference
 
-       app := refOpts.applicationCompat
+       app := refOpts.Application
        if app != nil {
                refOpts.metaDataType = app.MetadataType
                if refConf.Group == "" {
@@ -98,12 +95,11 @@ func (refOpts *ReferenceOptions) init(opts 
...ReferenceOption) error {
        }
 
        // init method
-       methods := refConf.MethodsConfig
-       if length := len(methods); length > 0 {
-               refOpts.methodsCompat = make([]*config.MethodConfig, length)
-               for i, method := range methods {
-                       refOpts.methodsCompat[i] = compatMethodConfig(method)
-                       if err := refOpts.methodsCompat[i].Init(); err != nil {
+       if refConf.MethodsConfig == nil {
+               refConf.MethodsConfig = make([]*global.MethodConfig, 0)
+       } else {
+               for _, method := range refConf.MethodsConfig {
+                       if err := internal.ValidateMethodConfig(method); err != 
nil {
                                return err
                        }
                }
@@ -115,36 +111,18 @@ func (refOpts *ReferenceOptions) init(opts 
...ReferenceOption) error {
        }
 
        // init registries
-       // convert Registries to registriesCompat
        if len(refOpts.Registries) > 0 {
-               if refOpts.registriesCompat == nil {
-                       refOpts.registriesCompat = 
make(map[string]*config.RegistryConfig)
-               }
-               for id, reg := range refOpts.Registries {
-                       refOpts.registriesCompat[id] = compatRegistryConfig(reg)
-                       if err := refOpts.registriesCompat[id].Init(); err != 
nil {
-                               return err
-                       }
-               }
-       }
-
-       if len(refOpts.registriesCompat) > 0 {
-               regs := refOpts.registriesCompat
+               regs := refOpts.Registries
                if len(refConf.RegistryIDs) <= 0 {
-                       refConf.RegistryIDs = make([]string, len(regs))
+                       refConf.RegistryIDs = make([]string, 0, len(regs))
                        for key := range regs {
                                refConf.RegistryIDs = 
append(refConf.RegistryIDs, key)
                        }
                }
                refConf.RegistryIDs = 
commonCfg.TranslateIds(refConf.RegistryIDs)
-
-               newRegs := make(map[string]*config.RegistryConfig)
-               for _, id := range refConf.RegistryIDs {
-                       if reg, ok := regs[id]; ok {
-                               newRegs[id] = reg
-                       }
+               if err := validateRegistryIDs(refConf.RegistryIDs, regs); err 
!= nil {
+                       return err
                }
-               refOpts.registriesCompat = newRegs
        }
 
        // init protocol
@@ -447,14 +425,15 @@ func WithMeshProviderPort(port int) ReferenceOption {
        }
 }
 
-func WithMethod(opts ...config.MethodOption) ReferenceOption {
-       regOpts := config.NewMethodOptions(opts...)
-
+func WithMethod(method *global.MethodConfig) ReferenceOption {
        return func(opts *ReferenceOptions) {
-               if len(opts.Reference.MethodsConfig) == 0 {
+               if method == nil {
+                       return
+               }
+               if opts.Reference.MethodsConfig == nil {
                        opts.Reference.MethodsConfig = 
make([]*global.MethodConfig, 0)
                }
-               opts.Reference.MethodsConfig = 
append(opts.Reference.MethodsConfig, regOpts.Method)
+               opts.Reference.MethodsConfig = 
append(opts.Reference.MethodsConfig, method)
        }
 }
 
@@ -482,18 +461,6 @@ func setInterfaceName(interfaceName string) 
ReferenceOption {
        }
 }
 
-func setApplicationCompat(app *config.ApplicationConfig) ReferenceOption {
-       return func(opts *ReferenceOptions) {
-               opts.applicationCompat = app
-       }
-}
-
-func setRegistriesCompat(regs map[string]*config.RegistryConfig) 
ReferenceOption {
-       return func(opts *ReferenceOptions) {
-               opts.registriesCompat = regs
-       }
-}
-
 func setConsumer(consumer *global.ConsumerConfig) ReferenceOption {
        return func(opts *ReferenceOptions) {
                opts.Consumer = consumer
@@ -556,9 +523,7 @@ type ClientOptions struct {
        TLS         *global.TLSConfig
        Protocols   map[string]*global.ProtocolConfig
 
-       overallReference  *global.ReferenceConfig
-       applicationCompat *config.ApplicationConfig
-       registriesCompat  map[string]*config.RegistryConfig
+       overallReference *global.ReferenceConfig
 }
 
 func defaultClientOptions() *ClientOptions {
@@ -585,34 +550,18 @@ func (cliOpts *ClientOptions) init(opts ...ClientOption) 
error {
 
        consumerConf := cliOpts.Consumer
 
-       // init application
-       application := cliOpts.Application
-       if application != nil {
-               cliOpts.applicationCompat = compatApplicationConfig(application)
-               if err := cliOpts.applicationCompat.Init(); err != nil {
-                       return err
-               }
-       }
-
        // init registries
        regs := cliOpts.Registries
-       if regs != nil {
-               cliOpts.registriesCompat = 
make(map[string]*config.RegistryConfig)
+       if len(regs) > 0 {
                if len(consumerConf.RegistryIDs) <= 0 {
-                       consumerConf.RegistryIDs = make([]string, len(regs))
+                       consumerConf.RegistryIDs = make([]string, 0, len(regs))
                        for key := range regs {
                                consumerConf.RegistryIDs = 
append(consumerConf.RegistryIDs, key)
                        }
                }
                consumerConf.RegistryIDs = 
commonCfg.TranslateIds(consumerConf.RegistryIDs)
-
-               for _, id := range consumerConf.RegistryIDs {
-                       if reg, ok := regs[id]; ok {
-                               cliOpts.registriesCompat[id] = 
compatRegistryConfig(reg)
-                               if err := cliOpts.registriesCompat[id].Init(); 
err != nil {
-                                       return err
-                               }
-                       }
+               if err := validateRegistryIDs(consumerConf.RegistryIDs, regs); 
err != nil {
+                       return err
                }
        }
 
@@ -1017,6 +966,15 @@ func newDefaultCallOptions() *CallOptions {
        return &CallOptions{}
 }
 
+func validateRegistryIDs(ids []string, regs map[string]*global.RegistryConfig) 
error {
+       for _, id := range ids {
+               if _, ok := regs[id]; !ok {
+                       return fmt.Errorf("registry id %q not found", id)
+               }
+       }
+       return nil
+}
+
 // WithCallRequestTimeout the maximum waiting time for one specific call, only 
works for 'tri' and 'dubbo' protocol
 func WithCallRequestTimeout(timeout time.Duration) CallOption {
        return func(opts *CallOptions) {
diff --git a/client/options_test.go b/client/options_test.go
index a05660eee..963d3696e 100644
--- a/client/options_test.go
+++ b/client/options_test.go
@@ -130,9 +130,7 @@ func TestWithClientRegistry(t *testing.T) {
                                reg, ok := 
cli.cliOpts.Registries[constant.ZookeeperKey]
                                assert.True(t, ok)
                                assert.Equal(t, "127.0.0.1:2181", reg.Address)
-                               regCompat, ok := 
cli.cliOpts.registriesCompat[constant.ZookeeperKey]
-                               assert.True(t, ok)
-                               assert.Equal(t, "127.0.0.1:2181", 
regCompat.Address)
+                               assert.Equal(t, 
[]string{constant.ZookeeperKey}, cli.cliOpts.Consumer.RegistryIDs)
                        },
                },
                {
@@ -149,9 +147,7 @@ func TestWithClientRegistry(t *testing.T) {
                                reg, ok := cli.cliOpts.Registries["zk"]
                                assert.True(t, ok)
                                assert.Equal(t, "127.0.0.1:2181", reg.Address)
-                               regCompat, ok := 
cli.cliOpts.registriesCompat["zk"]
-                               assert.True(t, ok)
-                               assert.Equal(t, "127.0.0.1:2181", 
regCompat.Address)
+                               assert.Equal(t, []string{"zk"}, 
cli.cliOpts.Consumer.RegistryIDs)
                        },
                },
                {
@@ -170,18 +166,34 @@ func TestWithClientRegistry(t *testing.T) {
                        },
                        verify: func(t *testing.T, cli *Client, err error) {
                                require.NoError(t, err)
-                               zkReg, ok := 
cli.cliOpts.Registries[constant.ZookeeperKey]
-                               assert.True(t, ok)
-                               assert.Equal(t, "127.0.0.1:2181", zkReg.Address)
                                ncReg, ok := 
cli.cliOpts.Registries["nacos_test"]
                                assert.True(t, ok)
                                assert.Equal(t, "127.0.0.1:8848", ncReg.Address)
-
-                               _, ok = 
cli.cliOpts.registriesCompat[constant.ZookeeperKey]
-                               assert.False(t, ok)
-                               ncCompat, ok := 
cli.cliOpts.registriesCompat["nacos_test"]
+                               assert.Len(t, cli.cliOpts.Registries, 2)
+                               _, ok = 
cli.cliOpts.Registries[constant.ZookeeperKey]
                                assert.True(t, ok)
-                               assert.Equal(t, "127.0.0.1:8848", 
ncCompat.Address)
+                               assert.Equal(t, []string{"nacos_test"}, 
cli.cliOpts.Consumer.RegistryIDs)
+                       },
+               },
+               {
+                       desc: "config multiple registries without setting 
RegistryIds",
+                       opts: []ClientOption{
+                               WithClientRegistry(
+                                       registry.WithZookeeper(),
+                                       registry.WithAddress("127.0.0.1:2181"),
+                               ),
+                               WithClientRegistry(
+                                       registry.WithID("nacos_test"),
+                                       registry.WithNacos(),
+                                       registry.WithAddress("127.0.0.1:8848"),
+                               ),
+                       },
+                       verify: func(t *testing.T, cli *Client, err error) {
+                               require.NoError(t, err)
+                               assert.Len(t, cli.cliOpts.Registries, 2)
+                               assert.Contains(t, cli.cliOpts.Registries, 
constant.ZookeeperKey)
+                               assert.Contains(t, cli.cliOpts.Registries, 
"nacos_test")
+                               assert.ElementsMatch(t, 
[]string{constant.ZookeeperKey, "nacos_test"}, cli.cliOpts.Consumer.RegistryIDs)
                        },
                },
        }
@@ -1225,3 +1237,59 @@ func TestInitWithConsumer(t *testing.T) {
                t.Errorf("fields not copied as expected: %+v", ref)
        }
 }
+
+func TestClientOptionsInitKeepsOriginalRegistries(t *testing.T) {
+       cliOpts := &ClientOptions{
+               Consumer: &global.ConsumerConfig{
+                       RegistryIDs: []string{"r1"},
+               },
+               Registries: map[string]*global.RegistryConfig{
+                       "r1": {Protocol: "mock", Address: "127.0.0.1:2181"},
+                       "r2": {Protocol: "mock", Address: "127.0.0.2:2181"},
+               },
+               overallReference: &global.ReferenceConfig{},
+       }
+
+       err := cliOpts.init()
+       require.NoError(t, err)
+       assert.Len(t, cliOpts.Registries, 2)
+       assert.Contains(t, cliOpts.Registries, "r1")
+       assert.Contains(t, cliOpts.Registries, "r2")
+}
+
+func TestClientOptionsInitFailsOnMissingRegistryID(t *testing.T) {
+       cliOpts := &ClientOptions{
+               Consumer: &global.ConsumerConfig{
+                       RegistryIDs: []string{"missing"},
+               },
+               Registries: map[string]*global.RegistryConfig{
+                       "r1": {Protocol: "mock", Address: "127.0.0.1:2181"},
+               },
+               overallReference: &global.ReferenceConfig{},
+       }
+
+       err := cliOpts.init()
+       require.Error(t, err)
+       assert.Contains(t, err.Error(), `registry id "missing" not found`)
+}
+
+func TestReferenceOptionsInitFailsOnMissingRegistryID(t *testing.T) {
+       refOpts := defaultReferenceOptions()
+       refOpts.Registries = map[string]*global.RegistryConfig{
+               "r1": {Protocol: "mock", Address: "127.0.0.1:2181"},
+       }
+
+       err := refOpts.init(WithRegistryIDs("missing"))
+       require.Error(t, err)
+       assert.Contains(t, err.Error(), `registry id "missing" not found`)
+}
+
+func TestReferenceOptionsInitFailsOnInvalidMethodConfig(t *testing.T) {
+       refOpts := defaultReferenceOptions()
+       err := refOpts.init(WithMethod(&global.MethodConfig{
+               Name:         "testMethod",
+               TpsLimitRate: "-1",
+       }))
+       require.Error(t, err)
+       assert.Contains(t, err.Error(), "tps.limit.rate")
+}
diff --git a/global/config_test.go b/global/config_test.go
index f9b547b6f..31314e5bf 100644
--- a/global/config_test.go
+++ b/global/config_test.go
@@ -1798,6 +1798,7 @@ func TestMethodConfigFields(t *testing.T) {
                assert.Equal(t, "10s", method.RequestTimeout)
        })
 }
+
 func TestMetricConfigClone(t *testing.T) {
        t.Run("clone_metrics_config", func(t *testing.T) {
                metrics := DefaultMetricsConfig()
diff --git a/internal/config.go b/internal/config.go
index 90ae62fff..0f3d286dc 100644
--- a/internal/config.go
+++ b/internal/config.go
@@ -21,6 +21,7 @@
 package internal
 
 import (
+       "fmt"
        "net/url"
        "strconv"
        "strings"
@@ -33,16 +34,26 @@ import (
 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/global"
 )
 
-func LoadRegistries(registryIds []string, registries 
map[string]*global.RegistryConfig, roleType common.RoleType) []*common.URL {
+func LoadRegistries(registryIds []string, registries 
map[string]*global.RegistryConfig, roleType common.RoleType) ([]*common.URL, 
error) {
        var registryURLs []*common.URL
+       targetAll := len(registryIds) == 0 || (len(registryIds) == 1 && 
registryIds[0] == "")
+
+       if !targetAll {
+               for _, id := range registryIds {
+                       if _, ok := registries[id]; !ok {
+                               return nil, fmt.Errorf("registry id %q not 
found", id)
+                       }
+               }
+       }
 
        for k, registryConf := range registries {
                target := false
 
-               if len(registryIds) == 0 || (len(registryIds) == 1 && 
registryIds[0] == "") {
+               if targetAll {
                        target = true
                } else {
                        for _, tr := range registryIds {
@@ -56,8 +67,7 @@ func LoadRegistries(registryIds []string, registries 
map[string]*global.Registry
                if target {
                        urls, err := toURLs(registryConf, roleType)
                        if err != nil {
-                               logger.Errorf("The registry id: %s url is 
invalid, error: %#v", k, err)
-                               continue
+                               return nil, fmt.Errorf("registry id %q url is 
invalid: %w", k, err)
                        }
 
                        for _, u := range urls {
@@ -72,7 +82,7 @@ func LoadRegistries(registryIds []string, registries 
map[string]*global.Registry
                }
        }
 
-       return registryURLs
+       return registryURLs, nil
 }
 
 func toURLs(registriesConfig *global.RegistryConfig, roleType common.RoleType) 
([]*common.URL, error) {
@@ -161,3 +171,38 @@ func getUrlMap(registriesConfig *global.RegistryConfig, 
roleType common.RoleType
 func clientNameID(protocol, address string) string {
        return strings.Join([]string{constant.RegistryConfigPrefix, protocol, 
address}, "-")
 }
+
+func ValidateMethodConfig(method *global.MethodConfig) error {
+       if method == nil {
+               return nil
+       }
+
+       qualifiedMethodName := method.InterfaceName + "#" + method.Name
+       if method.TpsLimitStrategy != "" {
+               if _, err := 
extension.GetTpsLimitStrategyCreator(method.TpsLimitStrategy); err != nil {
+                       return err
+               }
+       }
+
+       if method.TpsLimitInterval != "" {
+               tpsLimitInterval, err := 
strconv.ParseInt(method.TpsLimitInterval, 0, 0)
+               if err != nil {
+                       return fmt.Errorf("[MethodConfig] Cannot parse the 
configuration tps.limit.interval for method %s, please check your 
configuration", qualifiedMethodName)
+               }
+               if tpsLimitInterval < 0 {
+                       return fmt.Errorf("[MethodConfig] The configuration 
tps.limit.interval for %s must be positive, please check your configuration", 
qualifiedMethodName)
+               }
+       }
+
+       if method.TpsLimitRate != "" {
+               tpsLimitRate, err := strconv.ParseInt(method.TpsLimitRate, 0, 0)
+               if err != nil {
+                       return fmt.Errorf("[MethodConfig] Cannot parse the 
configuration tps.limit.rate for method %s, please check your configuration", 
qualifiedMethodName)
+               }
+               if tpsLimitRate < 0 {
+                       return fmt.Errorf("[MethodConfig] The configuration 
tps.limit.rate for method %s must be positive, please check your 
configuration", qualifiedMethodName)
+               }
+       }
+
+       return nil
+}
diff --git a/internal/config_test.go b/internal/config_test.go
index 515786ebf..15fdcf7e8 100644
--- a/internal/config_test.go
+++ b/internal/config_test.go
@@ -61,7 +61,8 @@ func TestLoadRegistries_AllOrEmptyIDs(t *testing.T) {
 
        for _, tt := range tests {
                t.Run(tt.name, func(t *testing.T) {
-                       urls := LoadRegistries(tt.ids, registries, 
common.CONSUMER)
+                       urls, err := LoadRegistries(tt.ids, registries, 
common.CONSUMER)
+                       require.NoError(t, err)
                        require.Len(t, urls, 3)
 
                        counts := map[string]int{}
@@ -92,14 +93,15 @@ func TestLoadRegistries_FilteredIDs(t *testing.T) {
                },
        }
 
-       urls := LoadRegistries([]string{"r1"}, registries, common.CONSUMER)
+       urls, err := LoadRegistries([]string{"r1"}, registries, common.CONSUMER)
+       require.NoError(t, err)
        require.Len(t, urls, 1)
        for _, u := range urls {
                assert.Equal(t, "r1", u.GetParam(constant.RegistryIdKey, ""))
        }
 }
 
-func TestLoadRegistries_SkipInvalidURL(t *testing.T) {
+func TestLoadRegistries_InvalidURL(t *testing.T) {
        registries := map[string]*global.RegistryConfig{
                "bad": {
                        Protocol: "mock",
@@ -109,8 +111,25 @@ func TestLoadRegistries_SkipInvalidURL(t *testing.T) {
                },
        }
 
-       urls := LoadRegistries([]string{"bad"}, registries, common.CONSUMER)
-       assert.Empty(t, urls)
+       urls, err := LoadRegistries([]string{"bad"}, registries, 
common.CONSUMER)
+       assert.Nil(t, urls)
+       require.Error(t, err)
+       assert.Contains(t, err.Error(), `registry id "bad" url is invalid`)
+}
+
+func TestLoadRegistries_MissingRegistryID(t *testing.T) {
+       registries := map[string]*global.RegistryConfig{
+               "r1": {
+                       Protocol: "mock",
+                       Timeout:  "2s",
+                       Address:  "127.0.0.1:2181",
+               },
+       }
+
+       urls, err := LoadRegistries([]string{"missing"}, registries, 
common.CONSUMER)
+       assert.Nil(t, urls)
+       require.Error(t, err)
+       assert.Contains(t, err.Error(), `registry id "missing" not found`)
 }
 
 func TestToURLs_EmptyOrNA(t *testing.T) {
@@ -254,3 +273,34 @@ func TestClientNameID(t *testing.T) {
        got := clientNameID("nacos", "127.0.0.1:8848")
        assert.Equal(t, "dubbo.registries-nacos-127.0.0.1:8848", got)
 }
+
+func TestValidateMethodConfig(t *testing.T) {
+       t.Run("valid method config", func(t *testing.T) {
+               method := &global.MethodConfig{
+                       Name:             "testMethod",
+                       TpsLimitRate:     "1",
+                       TpsLimitInterval: "10",
+               }
+               require.NoError(t, ValidateMethodConfig(method))
+       })
+
+       t.Run("negative tps rate", func(t *testing.T) {
+               method := &global.MethodConfig{
+                       Name:         "testMethod",
+                       TpsLimitRate: "-1",
+               }
+               err := ValidateMethodConfig(method)
+               require.Error(t, err)
+               assert.Contains(t, err.Error(), "tps.limit.rate")
+       })
+
+       t.Run("invalid tps interval", func(t *testing.T) {
+               method := &global.MethodConfig{
+                       Name:             "testMethod",
+                       TpsLimitInterval: "bad",
+               }
+               err := ValidateMethodConfig(method)
+               require.Error(t, err)
+               assert.Contains(t, err.Error(), "tps.limit.interval")
+       })
+}
diff --git a/server/action.go b/server/action.go
index a90136b20..e7d593157 100644
--- a/server/action.go
+++ b/server/action.go
@@ -141,7 +141,11 @@ func (svcOpts *ServiceOptions) Export() error {
 
        regUrls := make([]*common.URL, 0)
        if !svcConf.NotRegister {
-               regUrls = internal.LoadRegistries(svcConf.RegistryIDs, 
svcOpts.Registries, common.PROVIDER)
+               var err error
+               regUrls, err = internal.LoadRegistries(svcConf.RegistryIDs, 
svcOpts.Registries, common.PROVIDER)
+               if err != nil {
+                       return err
+               }
        }
 
        urlMap := svcOpts.getUrlMap()
diff --git a/server/action_test.go b/server/action_test.go
index dcb0331bb..f4a5c0af8 100644
--- a/server/action_test.go
+++ b/server/action_test.go
@@ -23,6 +23,7 @@ import (
 
 import (
        "github.com/stretchr/testify/assert"
+       "github.com/stretchr/testify/require"
 
        "go.uber.org/atomic"
 )
@@ -499,6 +500,26 @@ func TestSetRegistrySubURL(t *testing.T) {
        assert.NotNil(t, regURL.SubURL)
 }
 
+func TestExportReturnsRegistryLoadError(t *testing.T) {
+       svcOpts := &ServiceOptions{
+               Service: &global.ServiceConfig{
+                       Interface:   "com.example.Service",
+                       RegistryIDs: []string{"bad"},
+                       NotRegister: false,
+               },
+               Registries: map[string]*global.RegistryConfig{
+                       "bad": {
+                               Protocol: "mock",
+                               Address:  "127.0.0.1:bad",
+                       },
+               },
+       }
+
+       err := svcOpts.Export()
+       require.Error(t, err)
+       assert.Contains(t, err.Error(), `registry id "bad" url is invalid`)
+}
+
 // Test Unexport when exported
 func TestUnexportWhenExported(t *testing.T) {
        svcOpts := &ServiceOptions{
diff --git a/server/options.go b/server/options.go
index fb064a46d..62013bf8f 100644
--- a/server/options.go
+++ b/server/options.go
@@ -18,6 +18,7 @@
 package server
 
 import (
+       "fmt"
        "reflect"
        "strconv"
        "sync"
@@ -42,6 +43,7 @@ import (
        aslimiter "dubbo.apache.org/dubbo-go/v3/filter/adaptivesvc/limiter"
        "dubbo.apache.org/dubbo-go/v3/global"
        "dubbo.apache.org/dubbo-go/v3/graceful_shutdown"
+       "dubbo.apache.org/dubbo-go/v3/internal"
        "dubbo.apache.org/dubbo-go/v3/metrics/probe"
        "dubbo.apache.org/dubbo-go/v3/protocol"
        "dubbo.apache.org/dubbo-go/v3/protocol/base"
@@ -87,6 +89,9 @@ func (srvOpts *ServerOptions) init(opts ...ServerOption) 
error {
        if len(providerConf.RegistryIDs) <= 0 {
                providerConf.RegistryIDs = getRegistryIds(srvOpts.Registries)
        }
+       if err := validateRegistryIDs(providerConf.RegistryIDs, 
srvOpts.Registries); err != nil {
+               return err
+       }
 
        providerConf.ProtocolIDs = 
commonCfg.TranslateIds(providerConf.ProtocolIDs)
 
@@ -558,6 +563,8 @@ func (svcOpts *ServiceOptions) init(srv *Server, opts 
...ServiceOption) error {
        }
        if len(svc.RegistryIDs) <= 0 {
                svc.NotRegister = true
+       } else if err := validateRegistryIDs(svc.RegistryIDs, 
svc.RCRegistriesMap); err != nil {
+               return err
        }
 
        svc.ProtocolIDs = commonCfg.TranslateIds(svc.ProtocolIDs)
@@ -573,6 +580,11 @@ func (svcOpts *ServiceOptions) init(srv *Server, opts 
...ServiceOption) error {
        if svc.TracingKey == "" {
                svc.TracingKey = svcOpts.Provider.TracingKey
        }
+       for _, method := range svc.Methods {
+               if err := internal.ValidateMethodConfig(method); err != nil {
+                       return err
+               }
+       }
 
        err := svcOpts.check()
        if err != nil {
@@ -623,6 +635,15 @@ func WithFilter(filter string) ServiceOption {
        }
 }
 
+func validateRegistryIDs(ids []string, regs map[string]*global.RegistryConfig) 
error {
+       for _, id := range ids {
+               if _, ok := regs[id]; !ok {
+                       return fmt.Errorf("registry id %q not found", id)
+               }
+       }
+       return nil
+}
+
 // todo(DMwangnima): think about a more ideal configuration style
 func WithProtocolIDs(protocolIDs []string) ServiceOption {
        return func(cfg *ServiceOptions) {
@@ -871,6 +892,18 @@ func WithRegistry(opts ...registry.Option) ServiceOption {
        }
 }
 
+func WithMethod(method *global.MethodConfig) ServiceOption {
+       return func(opts *ServiceOptions) {
+               if method == nil {
+                       return
+               }
+               if opts.Service.Methods == nil {
+                       opts.Service.Methods = make([]*global.MethodConfig, 0)
+               }
+               opts.Service.Methods = append(opts.Service.Methods, method)
+       }
+}
+
 func WithParam(k, v string) ServiceOption {
        return func(opts *ServiceOptions) {
                if opts.Service.Params == nil {
diff --git a/server/options_test.go b/server/options_test.go
index 21d440653..97408d39f 100644
--- a/server/options_test.go
+++ b/server/options_test.go
@@ -63,6 +63,73 @@ func TestServerOptionsInitWithOptions(t *testing.T) {
        assert.Equal(t, "test-group", opts.Provider.Group)
 }
 
+func TestServerOptionsInitFailsOnMissingRegistryID(t *testing.T) {
+       opts := defaultServerOptions()
+       opts.Provider.RegistryIDs = []string{"missing"}
+       opts.Registries = map[string]*global.RegistryConfig{
+               "r1": {Protocol: "mock", Address: "127.0.0.1:2181"},
+       }
+
+       err := opts.init()
+       require.Error(t, err)
+       assert.Contains(t, err.Error(), `registry id "missing" not found`)
+}
+
+func TestServiceOptionsInitFailsOnMissingRegistryID(t *testing.T) {
+       srv := &Server{
+               cfg: &ServerOptions{
+                       Provider: &global.ProviderConfig{
+                               ProtocolIDs: []string{"triple"},
+                       },
+                       Registries: map[string]*global.RegistryConfig{
+                               "r1": {Protocol: "mock", Address: 
"127.0.0.1:2181"},
+                       },
+                       Protocols: map[string]*global.ProtocolConfig{
+                               "triple": {Name: "triple"},
+                       },
+                       Application: global.DefaultApplicationConfig(),
+               },
+       }
+       svcOpts := defaultServiceOptions()
+       svcOpts.Registries = srv.cfg.Registries
+       svcOpts.Protocols = srv.cfg.Protocols
+       svcOpts.Provider = srv.cfg.Provider
+
+       err := svcOpts.init(srv, func(opts *ServiceOptions) {
+               opts.Service.RegistryIDs = []string{"missing"}
+       })
+       require.Error(t, err)
+       assert.Contains(t, err.Error(), `registry id "missing" not found`)
+}
+
+func TestServiceOptionsInitFailsOnInvalidMethodConfig(t *testing.T) {
+       srv := &Server{
+               cfg: &ServerOptions{
+                       Provider: &global.ProviderConfig{
+                               ProtocolIDs: []string{"triple"},
+                       },
+                       Protocols: map[string]*global.ProtocolConfig{
+                               "triple": {Name: "triple"},
+                       },
+                       Application: global.DefaultApplicationConfig(),
+               },
+       }
+       svcOpts := defaultServiceOptions()
+       svcOpts.Protocols = srv.cfg.Protocols
+       svcOpts.Provider = srv.cfg.Provider
+
+       err := svcOpts.init(srv, func(opts *ServiceOptions) {
+               opts.Service.Methods = []*global.MethodConfig{
+                       {
+                               Name:         "testMethod",
+                               TpsLimitRate: "-1",
+                       },
+               }
+       })
+       require.Error(t, err)
+       assert.Contains(t, err.Error(), "tps.limit.rate")
+}
+
 // Test WithServerLoadBalanceConsistentHashing
 func TestWithServerLoadBalanceConsistentHashing(t *testing.T) {
        opts := defaultServerOptions()
@@ -805,6 +872,20 @@ func TestWithParam(t *testing.T) {
        assert.Equal(t, "value1", opts.Service.Params["key1"])
 }
 
+func TestWithMethod(t *testing.T) {
+       opts := defaultServiceOptions()
+       method := &global.MethodConfig{
+               Name:    "testMethod",
+               Retries: "3",
+       }
+
+       opt := WithMethod(method)
+       opt(opts)
+
+       require.Len(t, opts.Service.Methods, 1)
+       assert.Equal(t, method, opts.Service.Methods[0])
+}
+
 // Test WithParam creates params map if nil
 func TestWithParamCreateMap(t *testing.T) {
        opts := defaultServiceOptions()


Reply via email to