This is an automated email from the ASF dual-hosted git repository.

liujun pushed a commit to branch refactor-with-go-components-experimental
in repository https://gitbox.apache.org/repos/asf/dubbo-admin.git

commit 6c1c76c6217c2b285852017019005e77da58d94c
Author: chickenlj <[email protected]>
AuthorDate: Fri Aug 4 10:52:35 2023 +0800

    Add dubbo3 service discovery support
---
 conf/admin.yml                                     |   6 +-
 pkg/admin/cache/registry/extension.go              |   4 +-
 pkg/admin/cache/registry/kube/registry.go          |   7 +-
 pkg/admin/cache/registry/universal/registry.go     |   8 +-
 .../service_instances_changed_listener_impl.go     |  26 ++-
 pkg/admin/constant/const.go                        |   5 +
 pkg/admin/handlers/service.go                      | 100 ++++++++-
 pkg/admin/model/provider.go                        |  30 +--
 pkg/admin/model/registry_source.go                 |  26 ---
 pkg/admin/model/service_dto.go                     |  23 ++-
 pkg/admin/model/traffic.go                         |  48 ++++-
 pkg/admin/model/util/sync_utils.go                 |   4 +-
 pkg/admin/providers/mock/mock_provider.go          |   5 +-
 pkg/admin/router/router.go                         |   4 +
 pkg/admin/services/generic_service_Impl.go         |  64 ++++++
 pkg/admin/services/provider_service_impl.go        |   5 +-
 pkg/admin/services/registry_service_sync.go        |  74 ++++++-
 pkg/admin/services/service_testing_v3.go           | 223 +++++++++++++++++++++
 pkg/admin/services/traffic/accesslog.go            |   7 +-
 pkg/admin/services/traffic/argument.go             |   7 +-
 pkg/admin/services/traffic/mock.go                 |   7 +-
 pkg/admin/services/traffic/region.go               |   7 +-
 pkg/admin/services/traffic/retry.go                |  14 +-
 pkg/admin/services/traffic/timeout.go              |  21 +-
 pkg/admin/services/traffic/weight.go               |   7 +-
 pkg/config/admin/address_config.go                 |   9 +-
 pkg/config/config.go                               |   7 +-
 27 files changed, 636 insertions(+), 112 deletions(-)

diff --git a/conf/admin.yml b/conf/admin.yml
index f1d02e30..42d8bf8d 100644
--- a/conf/admin.yml
+++ b/conf/admin.yml
@@ -14,11 +14,11 @@
 # limitations under the License.
 admin:
   admin-port: 38080
-  config-center: zookeeper://127.0.0.1:2181
+  config-center: nacos://127.0.0.1:8848
   metadata-report:
-    address: zookeeper://127.0.0.1:2181
+    address: nacos://127.0.0.1:8848
   registry:
-    address: zookeeper://127.0.0.1:2181
+    address: nacos://127.0.0.1:8848
   prometheus:
     ip: 127.0.0.1
     port: 9090
diff --git a/pkg/admin/cache/registry/extension.go 
b/pkg/admin/cache/registry/extension.go
index 43c34554..79b7cee3 100644
--- a/pkg/admin/cache/registry/extension.go
+++ b/pkg/admin/cache/registry/extension.go
@@ -2,6 +2,7 @@ package registry
 
 import (
        "dubbo.apache.org/dubbo-go/v3/common"
+       dubboRegistry "dubbo.apache.org/dubbo-go/v3/registry"
 )
 
 var registries = make(map[string]func(u *common.URL) (AdminRegistry, error))
@@ -25,8 +26,9 @@ func Registry(name string, config *common.URL) 
(AdminRegistry, error) {
 type AdminRegistry interface {
        Subscribe(listener AdminNotifyListener) error
        Destroy() error
+       Delegate() dubboRegistry.Registry
 }
 
 type AdminNotifyListener interface {
-       Notify(url *common.URL)
+       Notify(event *dubboRegistry.ServiceEvent)
 }
diff --git a/pkg/admin/cache/registry/kube/registry.go 
b/pkg/admin/cache/registry/kube/registry.go
index 72468f6d..3cc489a5 100644
--- a/pkg/admin/cache/registry/kube/registry.go
+++ b/pkg/admin/cache/registry/kube/registry.go
@@ -2,12 +2,13 @@ package kube
 
 import (
        "dubbo.apache.org/dubbo-go/v3/common"
+       dubboRegistry "dubbo.apache.org/dubbo-go/v3/registry"
        "github.com/apache/dubbo-admin/pkg/admin/cache/registry"
        "github.com/apache/dubbo-admin/pkg/core/kubeclient/client"
 )
 
 func init() {
-       registry.AddRegistry("universal", func(u *common.URL) 
(registry.AdminRegistry, error) {
+       registry.AddRegistry("kube", func(u *common.URL) 
(registry.AdminRegistry, error) {
                return NewRegistry()
        })
 }
@@ -20,6 +21,10 @@ func NewRegistry() (*Registry, error) {
        return nil, nil
 }
 
+func (kr *Registry) Delegate() dubboRegistry.Registry {
+       return nil
+}
+
 func (kr *Registry) Subscribe(listener registry.AdminNotifyListener) error {
        return nil
 }
diff --git a/pkg/admin/cache/registry/universal/registry.go 
b/pkg/admin/cache/registry/universal/registry.go
index 9f731c9f..67139c75 100644
--- a/pkg/admin/cache/registry/universal/registry.go
+++ b/pkg/admin/cache/registry/universal/registry.go
@@ -68,6 +68,10 @@ func NewRegistry(delegate dubboRegistry.Registry, sdDelegate 
dubboRegistry.Servi
        }
 }
 
+func (kr *Registry) Delegate() dubboRegistry.Registry {
+       return kr.delegate
+}
+
 func (kr *Registry) Subscribe(listener registry.AdminNotifyListener) error {
        delRegistryListener := DubboRegistryNotifyListener{listener: listener}
        go func() {
@@ -115,9 +119,7 @@ type DubboRegistryNotifyListener struct {
 }
 
 func (l DubboRegistryNotifyListener) Notify(event *dubboRegistry.ServiceEvent) 
{
-       // TODO implement me
-       serviceURL := event.Service
-       l.listener.Notify(serviceURL)
+       l.listener.Notify(event)
 }
 
 func (l DubboRegistryNotifyListener) NotifyAll(events 
[]*dubboRegistry.ServiceEvent, f func()) {
diff --git 
a/pkg/admin/cache/registry/universal/service_instances_changed_listener_impl.go 
b/pkg/admin/cache/registry/universal/service_instances_changed_listener_impl.go
index b50bc716..8b1059aa 100644
--- 
a/pkg/admin/cache/registry/universal/service_instances_changed_listener_impl.go
+++ 
b/pkg/admin/cache/registry/universal/service_instances_changed_listener_impl.go
@@ -18,7 +18,9 @@
 package universal
 
 import (
+       "dubbo.apache.org/dubbo-go/v3/metadata/service/local"
        "reflect"
+       "sync"
 )
 
 import (
@@ -29,10 +31,12 @@ import (
 
 import (
        "dubbo.apache.org/dubbo-go/v3/common"
-       "dubbo.apache.org/dubbo-go/v3/common/constant"
+       dubboconstant "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"
+
+       "github.com/apache/dubbo-admin/pkg/admin/constant"
 )
 
 // DubboSDNotifyListener The Service Discovery Changed  Event Listener
@@ -42,6 +46,8 @@ type DubboSDNotifyListener struct {
        serviceUrls        map[string][]*common.URL
        revisionToMetadata map[string]*common.MetadataInfo
        allInstances       map[string][]registry.ServiceInstance
+
+       mutex sync.Mutex
 }
 
 func NewDubboSDNotifyListener(services *gxset.HashSet) 
registry.ServiceInstancesChangedListener {
@@ -61,6 +67,10 @@ func (lstn *DubboSDNotifyListener) OnEvent(e observer.Event) 
error {
                return nil
        }
        var err error
+
+       lstn.mutex.Lock()
+       defer lstn.mutex.Unlock()
+
        lstn.allInstances[ce.ServiceName] = ce.Instances
        revisionToInstances := make(map[string][]registry.ServiceInstance)
        newRevisionToMetadata := make(map[string]*common.MetadataInfo)
@@ -76,7 +86,7 @@ func (lstn *DubboSDNotifyListener) OnEvent(e observer.Event) 
error {
                                logger.Warnf("Instance metadata is nil: %s", 
instance.GetHost())
                                continue
                        }
-                       revision := 
instance.GetMetadata()[constant.ExportedServicesRevisionPropertyName]
+                       revision := 
instance.GetMetadata()[dubboconstant.ExportedServicesRevisionPropertyName]
                        if "0" == revision {
                                logger.Infof("Find instance without valid 
service metadata: %s", instance.GetHost())
                                continue
@@ -134,7 +144,7 @@ func (lstn *DubboSDNotifyListener) OnEvent(e 
observer.Event) error {
                        urls := lstn.serviceUrls[key]
                        events := make([]*registry.ServiceEvent, 0, len(urls))
                        for _, url := range urls {
-                               url.AddParam("TYPE", "instance")
+                               url.SetParam(constant.RegistryType, 
constant.RegistryInstance)
                                events = append(events, &registry.ServiceEvent{
                                        Action:  remoting.EventTypeAdd,
                                        Service: url,
@@ -151,6 +161,7 @@ func (lstn *DubboSDNotifyListener) 
AddListenerAndNotify(serviceKey string, notif
        lstn.listeners[serviceKey] = notify
        urls := lstn.serviceUrls[serviceKey]
        for _, url := range urls {
+               url.SetParam(constant.RegistryType, constant.RegistryInstance)
                notify.Notify(&registry.ServiceEvent{
                        Action:  remoting.EventTypeAdd,
                        Service: url,
@@ -191,11 +202,11 @@ func GetMetadataInfo(instance registry.ServiceInstance, 
revision string) (*commo
        var metadataStorageType string
        var metadataInfo *common.MetadataInfo
        if instance.GetMetadata() == nil {
-               metadataStorageType = constant.DefaultMetadataStorageType
+               metadataStorageType = dubboconstant.DefaultMetadataStorageType
        } else {
-               metadataStorageType = 
instance.GetMetadata()[constant.MetadataStorageTypePropertyName]
+               metadataStorageType = 
instance.GetMetadata()[dubboconstant.MetadataStorageTypePropertyName]
        }
-       if metadataStorageType == constant.RemoteMetadataStorageType {
+       if metadataStorageType == dubboconstant.RemoteMetadataStorageType {
                remoteMetadataServiceImpl, err := 
extension.GetRemoteMetadataService()
                if err != nil {
                        return nil, err
@@ -206,8 +217,9 @@ func GetMetadataInfo(instance registry.ServiceInstance, 
revision string) (*commo
                }
        } else {
                var err error
-               proxyFactory := 
extension.GetMetadataServiceProxyFactory(constant.DefaultKey)
+               proxyFactory := 
extension.GetMetadataServiceProxyFactory(dubboconstant.DefaultKey)
                metadataService := proxyFactory.GetProxy(instance)
+               defer 
metadataService.(*local.MetadataServiceProxy).Invoker.Destroy()
                metadataInfo, err = metadataService.GetMetadataInfo(revision)
                if err != nil {
                        return nil, err
diff --git a/pkg/admin/constant/const.go b/pkg/admin/constant/const.go
index 833a5976..405635f4 100644
--- a/pkg/admin/constant/const.go
+++ b/pkg/admin/constant/const.go
@@ -71,6 +71,11 @@ const (
        ConditionRuleSuffix    = ".condition-router"
        TagRuleSuffix          = ".tag-router"
        ConfigFileEnvKey       = "conf" // config file path
+       RegistryAll            = "ALL"
+       RegistryInterface      = "INTERFACE"
+       RegistryInstance       = "INSTANCE"
+       RegistryType           = "TYPE"
+       NamespaceKey           = "namespace"
 )
 
 var Configs = set.NewSet(WeightKey, BalancingKey)
diff --git a/pkg/admin/handlers/service.go b/pkg/admin/handlers/service.go
index 155ea64b..f13a76e7 100644
--- a/pkg/admin/handlers/service.go
+++ b/pkg/admin/handlers/service.go
@@ -18,12 +18,14 @@
 package handlers
 
 import (
+       "dubbo.apache.org/dubbo-go/v3/config/generic"
        "encoding/json"
-       "net/http"
-       "strconv"
-
        "github.com/apache/dubbo-admin/pkg/core/cmd/version"
        "github.com/apache/dubbo-admin/pkg/core/logger"
+       hessian "github.com/apache/dubbo-go-hessian2"
+       "net/http"
+       "strconv"
+       "time"
 
        "dubbo.apache.org/dubbo-go/v3/metadata/definition"
 
@@ -42,9 +44,11 @@ import (
 )
 
 var (
-       providerService services.ProviderService = 
&services.ProviderServiceImpl{}
-       consumerService services.ConsumerService = 
&services.ConsumerServiceImpl{}
-       monitorService  services.MonitorService  = 
&services.PrometheusServiceImpl{}
+       providerService    services.ProviderService     = 
&services.ProviderServiceImpl{}
+       consumerService    services.ConsumerService     = 
&services.ConsumerServiceImpl{}
+       monitorService     services.MonitorService      = 
&services.PrometheusServiceImpl{}
+       genericServiceImpl *services.GenericServiceImpl = 
&services.GenericServiceImpl{}
+       serviceTesting     *services.ServiceTestingV3   = 
&services.ServiceTestingV3{}
 )
 
 // AllServices get all services
@@ -317,3 +321,87 @@ func PromDiscovery(c *gin.Context) {
        }
        c.JSON(http.StatusOK, targets)
 }
+
+// Test works for dubbo2 tcp protocol
+func Test(c *gin.Context) {
+       var serviceTestDTO model.ServiceTest
+
+       err := c.BindJSON(&serviceTestDTO)
+       if err != nil {
+               c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
+               return
+       }
+       refConf := genericServiceImpl.NewRefConf("dubbo-admin", 
serviceTestDTO.Service, "dubbo")
+       time.Sleep(2 * time.Second)
+       resp, err := refConf.
+               GetRPCService().(*generic.GenericService).
+               Invoke(
+                       c,
+                       serviceTestDTO.Method,
+                       serviceTestDTO.ParameterTypes,
+                       []hessian.Object{"A003"}, //fixme
+               )
+       refConf.GetInvoker().Destroy()
+       if err != nil {
+               logger.Error("Error do generic invoke for service test", err)
+               c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
+               return
+       }
+       c.JSON(http.StatusOK, resp)
+}
+
+// HttpTest works for triple protocol
+func HttpTest(c *gin.Context) {
+       //pattern := c.Query("service")
+       //filter := c.Query("method")
+       //address := c.Query("address")
+
+       // send standard http request to backend http://address/service/method 
content-type:json
+
+       c.JSON(http.StatusOK, gin.H{
+               "code": 1,
+               "data": "implement me",
+       })
+}
+
+func MethodDetail(c *gin.Context) {
+       service := c.Query("service")
+       group := util.GetGroup(service)
+       version := util.GetVersion(service)
+       interfaze := util.GetInterface(service)
+       application := c.Query("application")
+       method := c.Query("method")
+
+       identifier := &identifier.MetadataIdentifier{
+               Application: application,
+               BaseMetadataIdentifier: identifier.BaseMetadataIdentifier{
+                       ServiceInterface: interfaze,
+                       Version:          version,
+                       Group:            group,
+                       Side:             constant.ProviderSide,
+               },
+       }
+       metadata, _ := 
config.MetadataReportCenter.GetServiceDefinition(identifier)
+       var methodMetadata model.MethodMetadata
+       if metadata != "" {
+               serviceDefinition := &definition.FullServiceDefinition{}
+               err := json.Unmarshal([]byte(metadata), &serviceDefinition)
+               if err != nil {
+                       c.JSON(http.StatusInternalServerError, gin.H{
+                               "error": err.Error(),
+                       })
+                       return
+               }
+               methods := serviceDefinition.Methods
+               if methods != nil {
+                       for _, m := range methods {
+                               if serviceTesting.SameMethod(m, method) {
+                                       methodMetadata = 
serviceTesting.GenerateMethodMeta(*serviceDefinition, m)
+                                       break
+                               }
+                       }
+               }
+       }
+
+       c.JSON(http.StatusOK, methodMetadata)
+}
diff --git a/pkg/admin/model/provider.go b/pkg/admin/model/provider.go
index 2acc17e4..e84c2d2c 100644
--- a/pkg/admin/model/provider.go
+++ b/pkg/admin/model/provider.go
@@ -19,19 +19,19 @@ import "time"
 
 type Provider struct {
        Entity
-       Service        string         `json:"service"`
-       URL            string         `json:"url"`
-       Parameters     string         `json:"parameters"`
-       Address        string         `json:"address"`
-       Registry       string         `json:"registry"`
-       Dynamic        bool           `json:"dynamic"`
-       Enabled        bool           `json:"enabled"`
-       Timeout        int64          `json:"timeout"`
-       Serialization  string         `json:"serialization"`
-       Weight         int64          `json:"weight"`
-       Application    string         `json:"application"`
-       Username       string         `json:"username"`
-       Expired        time.Duration  `json:"expired"`
-       Alived         int64          `json:"alived"`
-       RegistrySource RegistrySource `json:"registrySource"`
+       Service        string        `json:"service"`
+       URL            string        `json:"url"`
+       Parameters     string        `json:"parameters"`
+       Address        string        `json:"address"`
+       Registry       string        `json:"registry"`
+       Dynamic        bool          `json:"dynamic"`
+       Enabled        bool          `json:"enabled"`
+       Timeout        int64         `json:"timeout"`
+       Serialization  string        `json:"serialization"`
+       Weight         int64         `json:"weight"`
+       Application    string        `json:"application"`
+       Username       string        `json:"username"`
+       Expired        time.Duration `json:"expired"`
+       Alived         int64         `json:"alived"`
+       RegistrySource string        `json:"registrySource"`
 }
diff --git a/pkg/admin/model/registry_source.go 
b/pkg/admin/model/registry_source.go
deleted file mode 100644
index dff55390..00000000
--- a/pkg/admin/model/registry_source.go
+++ /dev/null
@@ -1,26 +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 model
-
-type RegistrySource string
-
-const (
-       All RegistrySource = "ALL"
-
-       Interface = "INTERFACE"
-
-       Instance = "INSTANCE"
-)
diff --git a/pkg/admin/model/service_dto.go b/pkg/admin/model/service_dto.go
index afa817a2..7d1b60ac 100644
--- a/pkg/admin/model/service_dto.go
+++ b/pkg/admin/model/service_dto.go
@@ -17,11 +17,11 @@ package model
 
 // ServiceDTO is the transforming format of service
 type ServiceDTO struct {
-       Service        string         `json:"service"`
-       AppName        string         `json:"appName"`
-       Group          string         `json:"group"`
-       Version        string         `json:"version"`
-       RegistrySource RegistrySource `json:"registrySource"`
+       Service        string `json:"service"`
+       AppName        string `json:"appName"`
+       Group          string `json:"group"`
+       Version        string `json:"version"`
+       RegistrySource string `json:"registrySource"`
 }
 
 type ListServiceByPage struct {
@@ -34,3 +34,16 @@ type ListServiceByPage struct {
        PageNumber    string        `json:"pageNumber"`
        Offset        int           `json:"offset"`
 }
+
+type ServiceTest struct {
+       Service        string        `json:"service"`
+       Method         string        `json:"method"`
+       ParameterTypes []string      `json:"ParameterTypes"`
+       Params         []interface{} `json:"params"`
+}
+
+type MethodMetadata struct {
+       Signature      string        `json:"signature"`
+       ParameterTypes []interface{} `json:"parameterTypes"`
+       ReturnType     string        `json:"returnType"`
+}
diff --git a/pkg/admin/model/traffic.go b/pkg/admin/model/traffic.go
index b3805a22..3f1292a6 100644
--- a/pkg/admin/model/traffic.go
+++ b/pkg/admin/model/traffic.go
@@ -49,6 +49,13 @@ func (t Timeout) ToRule() Override {
        }
 }
 
+func (t Timeout) GetKey() string {
+       interfaze := util.GetInterface(t.Service)
+       group := util.GetGroup(t.Service)
+       version := util.GetVersion(t.Service)
+       return util.ColonSeparatedKey(interfaze, group, version)
+}
+
 type Retry struct {
        Service string `json:"service" binding:"required"`
        Group   string `json:"group"`
@@ -69,6 +76,13 @@ func (t Retry) ToRule() Override {
        }
 }
 
+func (r Retry) GetKey() string {
+       interfaze := util.GetInterface(r.Service)
+       group := util.GetGroup(r.Service)
+       version := util.GetVersion(r.Service)
+       return util.ColonSeparatedKey(interfaze, group, version)
+}
+
 type Accesslog struct {
        Application string `json:"application" binding:"required"`
        Accesslog   string `json:"accesslog"`
@@ -106,6 +120,13 @@ func (r Region) ToRule() ConditionRoute {
        }
 }
 
+func (r Region) GetKey() string {
+       interfaze := util.GetInterface(r.Service)
+       group := util.GetGroup(r.Service)
+       version := util.GetVersion(r.Service)
+       return util.ColonSeparatedKey(interfaze, group, version)
+}
+
 type Gray struct {
        Application string `json:"application" binding:"required"`
        Tags        []Tag  `json:"tags" binding:"required"`
@@ -141,6 +162,13 @@ func (r Argument) ToRule() ConditionRoute {
        }
 }
 
+func (a Argument) GetKey() string {
+       interfaze := util.GetInterface(a.Service)
+       group := util.GetGroup(a.Service)
+       version := util.GetVersion(a.Service)
+       return util.ColonSeparatedKey(interfaze, group, version)
+}
+
 type Percentage struct {
        Service string   `json:"service" binding:"required"`
        Group   string   `json:"group"`
@@ -170,6 +198,13 @@ func (p Percentage) ToRule() Override {
        }
 }
 
+func (p Percentage) GetKey() string {
+       interfaze := util.GetInterface(p.Service)
+       group := util.GetGroup(p.Service)
+       version := util.GetVersion(p.Service)
+       return util.ColonSeparatedKey(interfaze, group, version)
+}
+
 type Mock struct {
        Service string `json:"service" binding:"required"`
        Group   string `json:"group"`
@@ -177,19 +212,26 @@ type Mock struct {
        Mock    string `json:"mock" binding:"required"`
 }
 
-func (t Mock) ToRule() Override {
+func (m Mock) ToRule() Override {
        return Override{
-               Key:           t.Service,
+               Key:           m.Service,
                Scope:         "service",
                ConfigVersion: "v3.0",
                Enabled:       true,
                Configs: []OverrideConfig{{
                        Side:       "consumer",
-                       Parameters: map[string]interface{}{"mock": t.Mock},
+                       Parameters: map[string]interface{}{"mock": m.Mock},
                }},
        }
 }
 
+func (m Mock) GetKey() string {
+       interfaze := util.GetInterface(m.Service)
+       group := util.GetGroup(m.Service)
+       version := util.GetVersion(m.Service)
+       return util.ColonSeparatedKey(interfaze, group, version)
+}
+
 type Host struct {
        Condition string `json:"condition" binding:"required"`
        Host      string `json:"host" binding:"required"`
diff --git a/pkg/admin/model/util/sync_utils.go 
b/pkg/admin/model/util/sync_utils.go
index cfd99bf1..446c3efe 100644
--- a/pkg/admin/model/util/sync_utils.go
+++ b/pkg/admin/model/util/sync_utils.go
@@ -53,7 +53,7 @@ func URL2Provider(id string, url *common.URL) *model.Provider 
{
                Timeout:        url.GetParamInt(constant.TimeoutKey, 
constant.DefaultTimeout),
                Weight:         url.GetParamInt(constant.WeightKey, 
constant.DefaultWeight),
                Username:       url.GetParam(constant.OwnerKey, ""),
-               RegistrySource: model.Interface,
+               RegistrySource: url.GetParam(constant.RegistryType, 
constant.RegistryInterface),
        }
 }
 
@@ -198,7 +198,7 @@ func Providers2DTO(providers []*model.Provider) 
[]*model.ServiceDTO {
        serviceDTOs := make([]*model.ServiceDTO, len(providers))
        for i := range providers {
                serviceDTOs[i] = &model.ServiceDTO{
-                       Service:        providers[i].Service,
+                       Service:        util.GetInterface(providers[i].Service),
                        AppName:        providers[i].Application,
                        Group:          util.GetGroup(providers[i].Service),
                        Version:        util.GetVersion(providers[i].Service),
diff --git a/pkg/admin/providers/mock/mock_provider.go 
b/pkg/admin/providers/mock/mock_provider.go
index 03a6f5a6..1b54a526 100644
--- a/pkg/admin/providers/mock/mock_provider.go
+++ b/pkg/admin/providers/mock/mock_provider.go
@@ -23,6 +23,7 @@ import (
 
        "github.com/apache/dubbo-admin/pkg/core/logger"
 
+       dubboconstant "dubbo.apache.org/dubbo-go/v3/common/constant"
        dubbogo "dubbo.apache.org/dubbo-go/v3/config"
        _ "dubbo.apache.org/dubbo-go/v3/imports"
        "github.com/apache/dubbo-admin/pkg/admin/mapper"
@@ -57,7 +58,9 @@ func RunMockServiceServer(admin admin.Admin, dubboConfig 
dubbogo.RootConfig) {
                mockRuleService: mockRuleService,
        })
 
-       builder := dubbogo.NewRootConfigBuilder().AddRegistry("zkRegistryKey", 
dubbogo.NewRegistryConfigBuilder().SetAddress(admin.Registry.Address).Build())
+       builder := dubbogo.NewRootConfigBuilder().
+               AddRegistry("zkRegistryKey", 
dubbogo.NewRegistryConfigBuilder().SetAddress(admin.Registry.Address).SetRegistryType(dubboconstant.RegistryTypeAll).
+                       
Build()).SetApplication(dubbogo.NewApplicationConfigBuilder().SetName("dubbo-admin").Build())
 
        for k, v := range dubboConfig.Protocols {
                builder.AddProtocol(k, 
dubbogo.NewProtocolConfigBuilder().SetName(v.Name).SetPort(v.Port).Build())
diff --git a/pkg/admin/router/router.go b/pkg/admin/router/router.go
index fd270526..6b3f2b92 100644
--- a/pkg/admin/router/router.go
+++ b/pkg/admin/router/router.go
@@ -200,6 +200,10 @@ func InitRouter() *Router {
                trafficRegion.GET("/", traffic.SearchRegion)
        }
 
+       // ServiceTest
+       router.POST("/api/:env/test", handlers.Test)
+       router.GET("/api/:env/test/method", handlers.MethodDetail)
+
        // Admin UI
        router.StaticFS("/admin", http.FS(ui.FS()))
 
diff --git a/pkg/admin/services/generic_service_Impl.go 
b/pkg/admin/services/generic_service_Impl.go
new file mode 100644
index 00000000..1798dcc3
--- /dev/null
+++ b/pkg/admin/services/generic_service_Impl.go
@@ -0,0 +1,64 @@
+// 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 services
+
+import (
+       "dubbo.apache.org/dubbo-go/v3/common"
+       dubboConfig "dubbo.apache.org/dubbo-go/v3/config"
+       "github.com/apache/dubbo-admin/pkg/admin/config"
+       "github.com/apache/dubbo-admin/pkg/admin/constant"
+       "github.com/apache/dubbo-admin/pkg/admin/util"
+
+       dubboconstant "dubbo.apache.org/dubbo-go/v3/common/constant"
+       dubboconfig "dubbo.apache.org/dubbo-go/v3/config"
+)
+
+type GenericServiceImpl struct{}
+
+func (p *GenericServiceImpl) NewRefConf(appName, iface, protocol string) 
dubboConfig.ReferenceConfig {
+
+       fromUrl := config.AdminRegistry.Delegate().GetURL().Clone()
+
+       registryConfig := dubboConfig.RegistryConfig{}
+       registryConfig.Group = fromUrl.GetParam("group", "")
+       address, _ := common.NewURL(fromUrl.Protocol + "://" + fromUrl.Location)
+       if fromUrl.GetParam(constant.NamespaceKey, "") != "" {
+               address.AddParam(constant.NamespaceKey, 
fromUrl.GetParam(constant.NamespaceKey, ""))
+       }
+       registryConfig.Address = address.String()
+       registryConfig.RegistryType = dubboconstant.RegistryTypeInterface
+
+       refConf := dubboConfig.ReferenceConfig{
+               InterfaceName: util.GetInterface(iface),
+               Group:         util.GetGroup(iface),
+               Version:       util.GetVersion(iface),
+               Cluster:       "failover",
+               RegistryIDs:   []string{"genericRegistry"},
+               Protocol:      protocol,
+               Generic:       "true",
+       }
+
+       rootConfig := dubboconfig.NewRootConfigBuilder().
+               AddRegistry("genericRegistry", &registryConfig).
+               Build()
+       if err := dubboconfig.Load(dubboconfig.WithRootConfig(rootConfig)); err 
!= nil {
+               panic(err)
+       }
+       _ = refConf.Init(rootConfig)
+       refConf.GenericLoad(appName)
+
+       return refConf
+}
diff --git a/pkg/admin/services/provider_service_impl.go 
b/pkg/admin/services/provider_service_impl.go
index 5e1d99a2..12a61bd4 100644
--- a/pkg/admin/services/provider_service_impl.go
+++ b/pkg/admin/services/provider_service_impl.go
@@ -284,6 +284,7 @@ func (p *ProviderServiceImpl) FindService(pattern string, 
filter string) ([]*mod
                reg       *regexp.Regexp
                err       error
        )
+       result := make([]*model.Provider, 0)
        if !strings.Contains(filter, constant.AnyValue) && 
!strings.Contains(filter, constant.InterrogationPoint) {
                if pattern == constant.IP {
                        providers, err = p.findByAddress(filter)
@@ -303,6 +304,7 @@ func (p *ProviderServiceImpl) FindService(pattern string, 
filter string) ([]*mod
                } else {
                        return nil, fmt.Errorf("unsupport the pattern: %s", 
pattern)
                }
+               result = providers
        } else {
                var candidates *set.HashSet
                if pattern == constant.IP {
@@ -352,11 +354,12 @@ func (p *ProviderServiceImpl) FindService(pattern string, 
filter string) ([]*mod
                                                return nil, err
                                        }
                                }
+                               result = append(result, providers...)
                        }
                }
        }
 
-       return util.Providers2DTO(providers), nil
+       return util.Providers2DTO(result), nil
 }
 
 func hasPrefixOrSuffix(filter string) bool {
diff --git a/pkg/admin/services/registry_service_sync.go 
b/pkg/admin/services/registry_service_sync.go
index 5748b3da..daadf8e9 100644
--- a/pkg/admin/services/registry_service_sync.go
+++ b/pkg/admin/services/registry_service_sync.go
@@ -18,6 +18,8 @@
 package services
 
 import (
+       dubboRegistry "dubbo.apache.org/dubbo-go/v3/registry"
+       "dubbo.apache.org/dubbo-go/v3/remoting"
        "github.com/apache/dubbo-admin/pkg/admin/cache/registry"
        "strings"
        "sync"
@@ -53,9 +55,10 @@ type adminNotifyListener struct {
        mu sync.Mutex
 }
 
-func (al *adminNotifyListener) Notify(serviceURL *common.URL) {
+func (al *adminNotifyListener) Notify(event *dubboRegistry.ServiceEvent) {
        var interfaceName string
        al.mu.Lock()
+       serviceURL := event.Service
        categories := make(map[string]map[string]map[string]*common.URL)
        category := serviceURL.GetParam(constant.CategoryKey, "")
        if len(category) == 0 {
@@ -66,7 +69,7 @@ func (al *adminNotifyListener) Notify(serviceURL *common.URL) 
{
                        category = constant.ProvidersCategory
                }
        }
-       if strings.EqualFold(constant.EmptyProtocol, serviceURL.Protocol) {
+       if event.Action == remoting.EventTypeDel {
                if services, ok := cache.InterfaceRegistryCache.Load(category); 
ok {
                        if services != nil {
                                servicesMap, ok := services.(*sync.Map)
@@ -78,14 +81,14 @@ func (al *adminNotifyListener) Notify(serviceURL 
*common.URL) {
                                group := serviceURL.Group()
                                version := serviceURL.Version()
                                if constant.AnyValue != group && 
constant.AnyValue != version {
-                                       servicesMap.Delete(serviceURL.Service())
+                                       deleteURL(servicesMap, serviceURL)
                                } else {
                                        // iterator services
                                        servicesMap.Range(func(key, value 
interface{}) bool {
                                                if 
util2.GetInterface(key.(string)) == serviceURL.Service() &&
                                                        (constant.AnyValue == 
group || group == util2.GetGroup(key.(string))) &&
                                                        (constant.AnyValue == 
version || version == util2.GetVersion(key.(string))) {
-                                                       servicesMap.Delete(key)
+                                                       deleteURL(servicesMap, 
serviceURL)
                                                }
                                                return true
                                        })
@@ -108,10 +111,10 @@ func (al *adminNotifyListener) Notify(serviceURL 
*common.URL) {
                        services[service] = ids
                }
                if md5, ok := UrlIdsMapper.Load(serviceURL.Key()); ok {
-                       ids[md5.(string)] = serviceURL
+                       ids[md5.(string)] = getURLToAdd(nil, serviceURL)
                } else {
                        md5 := util2.Md5_16bit(serviceURL.Key())
-                       ids[md5] = serviceURL
+                       ids[md5] = getURLToAdd(nil, serviceURL)
                        UrlIdsMapper.LoadOrStore(serviceURL.Key(), md5)
                }
        }
@@ -128,21 +131,72 @@ func (al *adminNotifyListener) Notify(serviceURL 
*common.URL) {
                                }
                                // iterator services key set
                                servicesMap.Range(func(key, inner any) bool {
-                                       _, ok := value[key.(string)]
+                                       ids, ok := value[key.(string)]
                                        if util2.GetInterface(key.(string)) == 
interfaceName && !ok {
                                                servicesMap.Delete(key)
+                                       } else {
+                                               for k, v := range ids {
+                                                       
inner.(map[string]*common.URL)[k] = 
getURLToAdd(inner.(map[string]*common.URL)[k], v)
+                                               }
                                        }
                                        return true
                                })
+
+                               for k, v := range value {
+                                       _, ok := services.(*sync.Map).Load(k)
+                                       if !ok {
+                                               services.(*sync.Map).Store(k, v)
+                                       }
+                               }
                        } else {
                                services = &sync.Map{}
                                cache.InterfaceRegistryCache.Store(category, 
services)
-                       }
-                       for k, v := range value {
-                               services.(*sync.Map).Store(k, v)
+                               for k, v := range value {
+                                       services.(*sync.Map).Store(k, v)
+                               }
                        }
                }
        }
 
        al.mu.Unlock()
 }
+
+func getURLToAdd(url *common.URL, newURL *common.URL) *common.URL {
+       if url == nil {
+               newURL = newURL.Clone()
+               if currentType, ok := 
newURL.GetNonDefaultParam(constant.RegistryType); !ok {
+                       newURL.SetParam(constant.RegistryType, 
constant.RegistryInterface)
+               } else {
+                       newURL.SetParam(constant.RegistryType, 
strings.ToUpper(currentType))
+               }
+       } else {
+               currentType, _ := url.GetNonDefaultParam(constant.RegistryType)
+               changedType, _ := 
newURL.GetNonDefaultParam(constant.RegistryType)
+               if currentType == constant.RegistryAll || currentType != 
changedType {
+                       newURL = newURL.Clone()
+                       newURL.SetParam(constant.RegistryType, 
constant.RegistryAll)
+               }
+       }
+       return newURL
+}
+
+func deleteURL(servicesMap *sync.Map, serviceURL *common.URL) {
+       if innerServices, ok := servicesMap.Load(serviceURL.Service()); ok {
+               innerServicesMap := innerServices.(map[string]*common.URL)
+               if len(innerServicesMap) > 0 {
+                       for _, url := range innerServicesMap {
+                               currentType, _ := 
url.GetNonDefaultParam(constant.RegistryType)
+                               changedType := 
serviceURL.GetParam(constant.RegistryType, constant.RegistryInterface)
+                               if currentType == constant.RegistryAll {
+                                       if changedType == 
constant.RegistryInstance {
+                                               
url.SetParam(constant.RegistryType, constant.RegistryInterface)
+                                       } else {
+                                               
url.SetParam(constant.RegistryType, constant.RegistryInstance)
+                                       }
+                               } else if currentType == changedType {
+                                       servicesMap.Delete(serviceURL.Service())
+                               }
+                       }
+               }
+       }
+}
diff --git a/pkg/admin/services/service_testing_v3.go 
b/pkg/admin/services/service_testing_v3.go
new file mode 100644
index 00000000..07fbb821
--- /dev/null
+++ b/pkg/admin/services/service_testing_v3.go
@@ -0,0 +1,223 @@
+// 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 services
+
+import (
+       "dubbo.apache.org/dubbo-go/v3/metadata/definition"
+       "github.com/apache/dubbo-admin/pkg/admin/model"
+       "regexp"
+       "strings"
+       "time"
+)
+
+var COLLECTION_PATTERN = 
regexp.MustCompile("^java\\.util\\..*(Set|List|Queue|Collection|Deque)(<.*>)*$")
+var MAP_PATTERN = regexp.MustCompile("^java\\.util\\..*Map.*(<.*>)*$")
+
+type ServiceTestingV3 struct{}
+
+func (p *ServiceTestingV3) SameMethod(m definition.MethodDefinition, methodSig 
string) bool {
+       name := m.Name
+       parameters := m.ParameterTypes
+       var sb strings.Builder
+       sb.WriteString(name)
+       sb.WriteString("~")
+       for _, parameter := range parameters {
+               sb.WriteString(parameter)
+               sb.WriteString(";")
+       }
+       sig := strings.TrimSuffix(sb.String(), ";")
+       return sig == methodSig
+}
+
+func (p *ServiceTestingV3) GenerateMethodMeta(serviceDefinition 
definition.FullServiceDefinition, methodDefinition definition.MethodDefinition) 
model.MethodMetadata {
+       var methodMetadata model.MethodMetadata
+       parameterTypes := methodDefinition.ParameterTypes
+       returnType := methodDefinition.ReturnType
+       signature := methodDefinition.Name + "~" + strings.Join(parameterTypes, 
";")
+       methodMetadata.Signature = signature
+       methodMetadata.ReturnType = returnType
+       parameters := p.GenerateParameterTypes(parameterTypes, 
serviceDefinition.ServiceDefinition)
+       methodMetadata.ParameterTypes = parameters
+       return methodMetadata
+}
+
+func (p *ServiceTestingV3) GenerateParameterTypes(parameterTypes []string, 
serviceDefinition definition.ServiceDefinition) []interface{} {
+       var parameters []interface{}
+       for _, tp := range parameterTypes {
+               result := p.GenerateType(serviceDefinition, tp)
+               parameters = append(parameters, result)
+       }
+       return parameters
+}
+
+func (p *ServiceTestingV3) FindTypeDefinition(serviceDefinition 
definition.ServiceDefinition, typeName string) definition.TypeDefinition {
+       for _, t := range serviceDefinition.Types {
+               if t.Type == typeName {
+                       return t
+               }
+       }
+       return definition.TypeDefinition{Type: typeName}
+}
+
+func (p *ServiceTestingV3) GenerateType(sd definition.ServiceDefinition, 
typeName string) interface{} {
+       td := p.FindTypeDefinition(sd, typeName)
+       return p.GenerateTypeHelper(sd, td)
+}
+
+func (p *ServiceTestingV3) GenerateTypeHelper(sd definition.ServiceDefinition, 
td definition.TypeDefinition) interface{} {
+       if p.IsPrimitiveType(td) {
+               return p.GeneratePrimitiveType(td)
+       } else if p.IsMap(td) {
+               return p.GenerateMapType(sd, td)
+       } else if p.IsArray(td) {
+               return p.GenerateArrayType(sd, td)
+       } else if p.IsCollection(td) {
+               return p.GenerateCollectionType(sd, td)
+       } else {
+               return p.GenerateComplexType(sd, td)
+       }
+}
+
+func (p *ServiceTestingV3) IsPrimitiveType(td definition.TypeDefinition) bool {
+       typeName := td.Type
+       return p.IsPrimitiveTypeHelper(typeName)
+}
+
+func (p *ServiceTestingV3) IsPrimitiveTypeHelper(typeName string) bool {
+       primitiveTypes := map[string]bool{
+               "byte":              true,
+               "java.lang.Byte":    true,
+               "short":             true,
+               "java.lang.Short":   true,
+               "int":               true,
+               "java.lang.Integer": true,
+               "long":              true,
+               "java.lang.Long":    true,
+               "float":             true,
+               "java.lang.Float":   true,
+               "double":            true,
+               "java.lang.Double":  true,
+               "boolean":           true,
+               "java.lang.Boolean": true,
+               "void":              true,
+               "java.lang.Void":    true,
+               "java.lang.String":  true,
+               "java.util.Date":    true,
+               "java.lang.Object":  true,
+       }
+       return primitiveTypes[typeName]
+}
+
+func (p *ServiceTestingV3) GeneratePrimitiveType(td definition.TypeDefinition) 
interface{} {
+       return p.GeneratePrimitiveTypeHelper(td.Type)
+}
+
+func (p *ServiceTestingV3) GeneratePrimitiveTypeHelper(typeName string) 
interface{} {
+       switch typeName {
+       case "byte", "java.lang.Byte", "short", "java.lang.Short",
+               "int", "java.lang.Integer", "long", "java.lang.Long":
+               return 0
+       case "float", "java.lang.Float", "double", "java.lang.Double":
+               return 0.0
+       case "boolean", "java.lang.Boolean":
+               return true
+       case "void", "java.lang.Void":
+               return nil
+       case "java.lang.String":
+               return ""
+       case "java.lang.Object":
+               return make(map[string]interface{})
+       case "java.util.Date":
+               return time.Now().UnixNano() / int64(time.Millisecond)
+       default:
+               return make(map[string]interface{})
+       }
+}
+
+func (p *ServiceTestingV3) IsMap(td definition.TypeDefinition) bool {
+       mapType := strings.Split(td.Type, "<")[0]
+       return MAP_PATTERN.MatchString(mapType)
+}
+
+func (p *ServiceTestingV3) GenerateMapType(sd definition.ServiceDefinition, td 
definition.TypeDefinition) interface{} {
+       keyType := strings.TrimSpace(strings.Split(strings.Split(td.Type, 
"<")[1], ",")[0])
+       key := p.GenerateType(sd, keyType)
+       valueType := strings.TrimSpace(strings.Split(strings.Split(td.Type, 
",")[1], ">")[0])
+       if valueType == "" {
+               valueType = "java.lang.Object"
+       }
+       value := p.GenerateType(sd, valueType)
+
+       mapObj := make(map[interface{}]interface{})
+       mapObj[key] = value
+       return mapObj
+}
+
+func (p *ServiceTestingV3) IsArray(td definition.TypeDefinition) bool {
+       return strings.HasSuffix(td.Type, "[]")
+}
+
+func (p *ServiceTestingV3) GenerateArrayType(sd definition.ServiceDefinition, 
td definition.TypeDefinition) interface{} {
+       typeStr := strings.TrimSuffix(td.Type, "[]")
+       elem := p.GenerateType(sd, typeStr)
+       return []interface{}{elem}
+}
+
+func (p *ServiceTestingV3) IsCollection(td definition.TypeDefinition) bool {
+       typeStr := strings.Split(td.Type, "<")[0]
+       return COLLECTION_PATTERN.MatchString(typeStr)
+}
+
+func (p *ServiceTestingV3) GenerateCollectionType(sd 
definition.ServiceDefinition, td definition.TypeDefinition) interface{} {
+       typeStr := strings.SplitAfterN(td.Type, "<", 2)[1]
+       if typeStr == "" {
+               // if type is null return empty collection
+               return []interface{}{}
+       }
+       elem := p.GenerateType(sd, typeStr)
+       return []interface{}{elem}
+}
+
+func (p *ServiceTestingV3) GenerateComplexType(sd 
definition.ServiceDefinition, td definition.TypeDefinition) interface{} {
+       holder := make(map[string]interface{})
+       p.GenerateComplexTypeHelper(sd, td, holder)
+       return holder
+}
+
+func (p *ServiceTestingV3) GenerateComplexTypeHelper(sd 
definition.ServiceDefinition, td definition.TypeDefinition, holder 
map[string]interface{}) {
+       for name, property := range td.Properties {
+               if p.IsPrimitiveType(property) {
+                       holder[name] = p.GeneratePrimitiveType(property)
+               } else {
+                       p.GenerateEnclosedType(holder, name, sd, property.Type)
+               }
+       }
+}
+
+func (p *ServiceTestingV3) GenerateEnclosedType(holder map[string]interface{}, 
key string, sd definition.ServiceDefinition, typeName string) {
+       if p.IsPrimitiveTypeHelper(typeName) {
+               holder[key] = p.GenerateType(sd, typeName)
+       } else {
+               td := p.FindTypeDefinition(sd, typeName)
+               if td.Properties == nil || len(td.Properties) == 0 {
+                       holder[key] = p.GenerateTypeHelper(sd, td)
+               } else {
+                       enclosedMap := make(map[string]interface{})
+                       holder[key] = enclosedMap
+                       p.GenerateComplexTypeHelper(sd, td, enclosedMap)
+               }
+       }
+}
diff --git a/pkg/admin/services/traffic/accesslog.go 
b/pkg/admin/services/traffic/accesslog.go
index 4c6c502d..14f774da 100644
--- a/pkg/admin/services/traffic/accesslog.go
+++ b/pkg/admin/services/traffic/accesslog.go
@@ -22,6 +22,7 @@ import (
        "github.com/apache/dubbo-admin/pkg/admin/model"
        "github.com/apache/dubbo-admin/pkg/admin/services"
        "gopkg.in/yaml.v2"
+       "strconv"
 )
 
 type AccesslogService struct{}
@@ -74,7 +75,11 @@ func (tm *AccesslogService) Search(a *model.Accesslog) 
([]*model.Accesslog, erro
                        accesslog := &model.Accesslog{
                                Application: override.Key,
                        }
-                       accesslog.Accesslog = alv.(string)
+                       if alvBool, ok := alv.(bool); ok {
+                               accesslog.Accesslog = 
strconv.FormatBool(alvBool)
+                       } else {
+                               accesslog.Accesslog = alv.(string)
+                       }
                        result = append(result, accesslog)
                }
        }
diff --git a/pkg/admin/services/traffic/argument.go 
b/pkg/admin/services/traffic/argument.go
index 6872caaa..15bbb8bf 100644
--- a/pkg/admin/services/traffic/argument.go
+++ b/pkg/admin/services/traffic/argument.go
@@ -23,7 +23,6 @@ import (
        "github.com/apache/dubbo-admin/pkg/admin/constant"
        "github.com/apache/dubbo-admin/pkg/admin/model"
        "github.com/apache/dubbo-admin/pkg/admin/services"
-       "github.com/apache/dubbo-admin/pkg/admin/util"
        "gopkg.in/yaml.v2"
 )
 
@@ -31,7 +30,7 @@ type ArgumentService struct{}
 
 // CreateOrUpdate create or update timeout rule
 func (tm *ArgumentService) CreateOrUpdate(a *model.Argument) error {
-       key := services.GetRoutePath(util.ColonSeparatedKey(a.Service, a.Group, 
a.Version), constant.ConditionRoute)
+       key := services.GetRoutePath(a.GetKey(), constant.ConditionRoute)
        newRule := a.ToRule()
 
        err := createOrUpdateCondition(key, newRule)
@@ -39,7 +38,7 @@ func (tm *ArgumentService) CreateOrUpdate(a *model.Argument) 
error {
 }
 
 func (tm *ArgumentService) Delete(a *model.Argument) error {
-       key := services.GetRoutePath(util.ColonSeparatedKey(a.Service, a.Group, 
a.Version), constant.ConditionRoute)
+       key := services.GetRoutePath(a.GetKey(), constant.ConditionRoute)
        err2 := removeCondition(key, a.Rule, model.RegionAdminIdentifier)
        if err2 != nil {
                return err2
@@ -52,7 +51,7 @@ func (tm *ArgumentService) Search(a *model.Argument) 
([]*model.Argument, error)
 
        var con string
        if a.Service != "" && a.Service != "*" {
-               con = util.ColonSeparatedKey(a.Service, a.Group, a.Version)
+               con = a.GetKey()
        }
 
        list, err := services.GetRules(con, constant.ConditionRuleSuffix)
diff --git a/pkg/admin/services/traffic/mock.go 
b/pkg/admin/services/traffic/mock.go
index bcdac0e9..6aaee7bf 100644
--- a/pkg/admin/services/traffic/mock.go
+++ b/pkg/admin/services/traffic/mock.go
@@ -23,14 +23,13 @@ import (
        "github.com/apache/dubbo-admin/pkg/admin/constant"
        "github.com/apache/dubbo-admin/pkg/admin/model"
        "github.com/apache/dubbo-admin/pkg/admin/services"
-       "github.com/apache/dubbo-admin/pkg/admin/util"
 )
 
 type MockService struct{}
 
 // CreateOrUpdate create or update timeout rule
 func (tm *MockService) CreateOrUpdate(m *model.Mock) error {
-       key := services.GetOverridePath(util.ColonSeparatedKey(m.Service, 
m.Group, m.Version))
+       key := services.GetOverridePath(m.GetKey())
        newRule := m.ToRule()
 
        err := createOrUpdateOverride(key, "consumer", "mock", newRule)
@@ -38,7 +37,7 @@ func (tm *MockService) CreateOrUpdate(m *model.Mock) error {
 }
 
 func (tm *MockService) Delete(m *model.Mock) error {
-       key := services.GetOverridePath(util.ColonSeparatedKey(m.Service, 
m.Group, m.Version))
+       key := services.GetOverridePath(m.GetKey())
        err2 := removeFromOverride(key, "consumer", "mock")
        if err2 != nil {
                return err2
@@ -51,7 +50,7 @@ func (tm *MockService) Search(m *model.Mock) ([]*model.Mock, 
error) {
 
        var con string
        if m.Service != "" && m.Service != "*" {
-               con = util.ColonSeparatedKey(m.Service, m.Group, m.Version)
+               con = m.GetKey()
        }
        list, err := services.GetRules(con, constant.ConfiguratorRuleSuffix)
        if err != nil {
diff --git a/pkg/admin/services/traffic/region.go 
b/pkg/admin/services/traffic/region.go
index 5705b2d4..bd4eaadc 100644
--- a/pkg/admin/services/traffic/region.go
+++ b/pkg/admin/services/traffic/region.go
@@ -23,7 +23,6 @@ import (
        "github.com/apache/dubbo-admin/pkg/admin/constant"
        "github.com/apache/dubbo-admin/pkg/admin/model"
        "github.com/apache/dubbo-admin/pkg/admin/services"
-       "github.com/apache/dubbo-admin/pkg/admin/util"
        "gopkg.in/yaml.v2"
 )
 
@@ -31,7 +30,7 @@ type RegionService struct{}
 
 // CreateOrUpdate create or update timeout rule
 func (tm *RegionService) CreateOrUpdate(r *model.Region) error {
-       key := services.GetRoutePath(util.ColonSeparatedKey(r.Service, r.Group, 
r.Version), constant.ConditionRoute)
+       key := services.GetRoutePath(r.GetKey(), constant.ConditionRoute)
        newRule := r.ToRule()
 
        var err error
@@ -45,7 +44,7 @@ func (tm *RegionService) CreateOrUpdate(r *model.Region) 
error {
 }
 
 func (tm *RegionService) Delete(r *model.Region) error {
-       key := services.GetRoutePath(util.ColonSeparatedKey(r.Service, r.Group, 
r.Version), constant.ConditionRoute)
+       key := services.GetRoutePath(r.GetKey(), constant.ConditionRoute)
        err2 := removeCondition(key, r.Rule, model.RegionAdminIdentifier)
        if err2 != nil {
                return err2
@@ -58,7 +57,7 @@ func (tm *RegionService) Search(r *model.Region) 
([]*model.Region, error) {
 
        var con string
        if r.Service != "" && r.Service != "*" {
-               con = util.ColonSeparatedKey(r.Service, r.Group, r.Version)
+               con = r.GetKey()
        }
 
        list, err := services.GetRules(con, constant.ConditionRuleSuffix)
diff --git a/pkg/admin/services/traffic/retry.go 
b/pkg/admin/services/traffic/retry.go
index 15b73c3c..9e15f9d0 100644
--- a/pkg/admin/services/traffic/retry.go
+++ b/pkg/admin/services/traffic/retry.go
@@ -18,6 +18,9 @@
 package traffic
 
 import (
+       "fmt"
+       "github.com/apache/dubbo-admin/pkg/core/logger"
+       "strconv"
        "strings"
 
        "github.com/apache/dubbo-admin/pkg/admin/constant"
@@ -77,7 +80,16 @@ func (tm *RetryService) Search(r *model.Retry) 
([]*model.Retry, error) {
                        return result, err2
                }
                if rv != nil {
-                       retry.Retry = rv.(int)
+                       if rvStr, ok := rv.(string); ok {
+                               rvInt, err := strconv.Atoi(rvStr)
+                               if err != nil {
+                                       logger.Error(fmt.Sprintf("Error parsing 
retry rule %s", v), err)
+                                       return result, err
+                               }
+                               retry.Retry = rvInt
+                       } else {
+                               retry.Retry = rv.(int)
+                       }
                        result = append(result, retry)
                }
        }
diff --git a/pkg/admin/services/traffic/timeout.go 
b/pkg/admin/services/traffic/timeout.go
index f17e4ca8..abe0a2f6 100644
--- a/pkg/admin/services/traffic/timeout.go
+++ b/pkg/admin/services/traffic/timeout.go
@@ -18,19 +18,21 @@
 package traffic
 
 import (
+       "fmt"
+       "github.com/apache/dubbo-admin/pkg/core/logger"
+       "strconv"
        "strings"
 
        "github.com/apache/dubbo-admin/pkg/admin/constant"
        "github.com/apache/dubbo-admin/pkg/admin/model"
        "github.com/apache/dubbo-admin/pkg/admin/services"
-       "github.com/apache/dubbo-admin/pkg/admin/util"
 )
 
 type TimeoutService struct{}
 
 // CreateOrUpdate create or update timeout rule
 func (tm *TimeoutService) CreateOrUpdate(t *model.Timeout) error {
-       key := services.GetOverridePath(util.ColonSeparatedKey(t.Service, 
t.Group, t.Version))
+       key := services.GetOverridePath(t.GetKey())
        newRule := t.ToRule()
 
        err := createOrUpdateOverride(key, "consumer", "timeout", newRule)
@@ -38,7 +40,7 @@ func (tm *TimeoutService) CreateOrUpdate(t *model.Timeout) 
error {
 }
 
 func (tm *TimeoutService) Delete(t *model.Timeout) error {
-       key := services.GetOverridePath(util.ColonSeparatedKey(t.Service, 
t.Group, t.Version))
+       key := services.GetOverridePath(t.GetKey())
        err2 := removeFromOverride(key, "consumer", "timeout")
        if err2 != nil {
                return err2
@@ -51,7 +53,7 @@ func (tm *TimeoutService) Search(t *model.Timeout) 
([]*model.Timeout, error) {
 
        var con string
        if t.Service != "" && t.Service != "*" {
-               con = util.ColonSeparatedKey(t.Service, t.Group, t.Version)
+               con = t.GetKey()
        }
 
        list, err := services.GetRules(con, constant.ConfiguratorRuleSuffix)
@@ -79,7 +81,16 @@ func (tm *TimeoutService) Search(t *model.Timeout) 
([]*model.Timeout, error) {
                }
 
                if tv != nil {
-                       t.Timeout = tv.(int)
+                       if tvStr, ok := tv.(string); ok {
+                               tvInt, err := strconv.Atoi(tvStr)
+                               if err != nil {
+                                       logger.Error(fmt.Sprintf("Error parsing 
timeout rule %s", v), err)
+                                       return result, err
+                               }
+                               t.Timeout = tvInt
+                       } else {
+                               t.Timeout = tv.(int)
+                       }
                        result = append(result, t)
                }
        }
diff --git a/pkg/admin/services/traffic/weight.go 
b/pkg/admin/services/traffic/weight.go
index 6384ba45..09882cce 100644
--- a/pkg/admin/services/traffic/weight.go
+++ b/pkg/admin/services/traffic/weight.go
@@ -23,7 +23,6 @@ import (
        "github.com/apache/dubbo-admin/pkg/admin/constant"
        "github.com/apache/dubbo-admin/pkg/admin/model"
        "github.com/apache/dubbo-admin/pkg/admin/services"
-       "github.com/apache/dubbo-admin/pkg/admin/util"
        "gopkg.in/yaml.v2"
 )
 
@@ -31,7 +30,7 @@ type WeightService struct{}
 
 // CreateOrUpdate create or update timeout rule
 func (tm *WeightService) CreateOrUpdate(p *model.Percentage) error {
-       key := services.GetOverridePath(util.ColonSeparatedKey(p.Service, 
p.Group, p.Version))
+       key := services.GetOverridePath(p.GetKey())
        newRule := p.ToRule()
 
        err := createOrUpdateOverride(key, "provider", "weight", newRule)
@@ -39,7 +38,7 @@ func (tm *WeightService) CreateOrUpdate(p *model.Percentage) 
error {
 }
 
 func (tm *WeightService) Delete(p *model.Percentage) error {
-       key := services.GetOverridePath(util.ColonSeparatedKey(p.Service, 
p.Group, p.Version))
+       key := services.GetOverridePath(p.GetKey())
        err := removeFromOverride(key, "provider", "weight")
        if err != nil {
                return err
@@ -52,7 +51,7 @@ func (tm *WeightService) Search(p *model.Percentage) 
([]*model.Percentage, error
 
        var con string
        if p.Service != "" && p.Service != "*" {
-               con = util.ColonSeparatedKey(p.Service, p.Group, p.Version)
+               con = p.GetKey()
        }
 
        list, err := services.GetRules(con, constant.ConfiguratorRuleSuffix)
diff --git a/pkg/config/admin/address_config.go 
b/pkg/config/admin/address_config.go
index 5885cd6f..98bae69e 100644
--- a/pkg/config/admin/address_config.go
+++ b/pkg/config/admin/address_config.go
@@ -19,6 +19,7 @@ package admin
 
 import (
        "net/url"
+       "strings"
 
        "dubbo.apache.org/dubbo-go/v3/common"
        "dubbo.apache.org/dubbo-go/v3/common/constant"
@@ -46,7 +47,9 @@ func (c *AddressConfig) GetAddress() string {
 func (c *AddressConfig) GetUrlMap() url.Values {
        urlMap := url.Values{}
        urlMap.Set(constant.ConfigNamespaceKey, c.param("namespace", ""))
-       urlMap.Set(constant.ConfigGroupKey, c.param("group", ""))
+       urlMap.Set(constant.ConfigGroupKey, c.param(constant.GroupKey, "dubbo"))
+       urlMap.Set(constant.MetadataReportGroupKey, c.param(constant.GroupKey, 
"dubbo"))
+       urlMap.Set(constant.ClientNameKey, clientNameID(c.Url.Scheme, 
c.Url.Host))
        return urlMap
 }
 
@@ -67,3 +70,7 @@ func (c *AddressConfig) ToURL() (*common.URL, error) {
                common.WithPassword(c.param("password", "")),
        )
 }
+
+func clientNameID(protocol, address string) string {
+       return strings.Join([]string{protocol, address}, "-")
+}
diff --git a/pkg/config/config.go b/pkg/config/config.go
index 9e5dab81..81b8acbb 100644
--- a/pkg/config/config.go
+++ b/pkg/config/config.go
@@ -18,10 +18,9 @@
 package config
 
 const (
-       SanitizedValue   = "*****"
-       conf             = "./conf/admin.yml"
-       confPathKey      = "ADMIN_CONFIG_PATH"
-       MockProviderConf = "./conf/mock_provider.yml"
+       SanitizedValue = "*****"
+       conf           = "./conf/admin.yml"
+       confPathKey    = "ADMIN_CONFIG_PATH"
 )
 
 type Config interface {

Reply via email to