vipyxc-byte commented on issue #3153:
URL: https://github.com/apache/dubbo-go/issues/3153#issuecomment-3775913343

   > Could you provide a reproducible code demo? I failed to reproduce this bug.
   
   抱歉,因公司规范,无法直接提供代码仓库或者压缩包,只能以这种形式提供,代码在下面。
   
   # 背景
   k8s集群下多次发布、重启服务提供者会导致泛化调用报 session Already Closed,之后 dubbo-go 
一直连接旧地址,这是最初的问题。
   
   所以我认为在 session Already Closed 时主动销毁 ReferenceConfig 再重新构建可以解决问题。
   
   但是在并发销毁/重新构建 ReferenceConfig 时,偶尔会出现 panic,Alanxtl 无法复现 session Already 
Closed,但修复了偶发的 panic 问题。
   
   如果你能解决 session Already Closed 问题,我就不用使用我的方案了。
   
   另外我认为重新构建 ReferenceConfig 是合理的操作,不应该出现 panic。以下是 3.3.1 版本仍然存在的 panic。
   
   最初的问题:https://github.com/apache/dubbo-go/issues/2981
   
   现在的问题:https://github.com/apache/dubbo-go/issues/3153
   
   ## 2026-01-05 panic
   panic: runtime error: invalid memory address or nil pointer dereference
   [signal SIGSEGV: segmentation violation code=0x2 addr=0x0 pc=0x102a668f8]
   
   goroutine 3809079 [running]:
   dubbo.apache.org/dubbo-go/v3/common.(*URL).GetParam(0x0, {0x1045bd638, 0xb}, 
{0x0, 0x0})
   /Users/dev/go/pkg/mod/dubbo.apache.org/dubbo-go/[email protected]/common/url.go:608 
+0x38
   
dubbo.apache.org/dubbo-go/v3/cluster/router/tag.(*PriorityRouter).Notify(0x1414fe18c60,
 {0x140b6945170, 0x1, 0x1})
   
/Users/dev/go/pkg/mod/dubbo.apache.org/dubbo-go/[email protected]/cluster/router/tag/router.go:80
 +0x84
   
dubbo.apache.org/dubbo-go/v3/cluster/router/chain.(*RouterChain).SetInvokers(0x140f6b38720,
 {0x140b6945170, 0x1, 0x1})
   
/Users/dev/go/pkg/mod/dubbo.apache.org/dubbo-go/[email protected]/cluster/router/chain/chain.go:95
 +0x1a4
   
dubbo.apache.org/dubbo-go/v3/registry/directory.(*RegistryDirectory).setNewInvokers(0x140b0e65d90)
   
/Users/dev/go/pkg/mod/dubbo.apache.org/dubbo-go/[email protected]/registry/directory/directory.go:344
 +0x148
   
dubbo.apache.org/dubbo-go/v3/registry/directory.(*RegistryDirectory).refreshInvokers(0x140b0e65d90,
 0x1416408ea80)
   
/Users/dev/go/pkg/mod/dubbo.apache.org/dubbo-go/[email protected]/registry/directory/directory.go:241
 +0x1b4
   
dubbo.apache.org/dubbo-go/v3/registry/directory.(*RegistryDirectory).Notify(0x140b0e65d90,
 0x1416408ea80)
   
/Users/dev/go/pkg/mod/dubbo.apache.org/dubbo-go/[email protected]/registry/directory/directory.go:219
 +0x44
   
dubbo.apache.org/dubbo-go/v3/registry.(*BaseRegistry).Subscribe(0x14000ab90e0, 
0x140b14d61c0, {0x104f49fd0, 0x140b0e65d90})
   
/Users/dev/go/pkg/mod/dubbo.apache.org/dubbo-go/[email protected]/registry/base_registry.go:354
 +0x44c
   
dubbo.apache.org/dubbo-go/v3/registry/directory.(*RegistryDirectory).Subscribe.func1()
   
/Users/dev/go/pkg/mod/dubbo.apache.org/dubbo-go/[email protected]/registry/directory/directory.go:167
 +0x8c
   created by 
dubbo.apache.org/dubbo-go/v3/registry/directory.(*RegistryDirectory).Subscribe 
in goroutine 1640288
   
/Users/dev/go/pkg/mod/dubbo.apache.org/dubbo-go/[email protected]/registry/directory/directory.go:165
 +0x160
   
   ## 2026-01-20 此代码复现 panic
   panic: runtime error: invalid memory address or nil pointer dereference
   [signal SIGSEGV: segmentation violation code=0x2 addr=0x0 pc=0x1047b1108]
   
   goroutine 271982 [running]:
   dubbo.apache.org/dubbo-go/v3/common.(*URL).GetParam(0x0, {0x105b4f87c, 0x5}, 
{0x0, 0x0})
   
/Users/yangzhen/dev/go/pkg/mod/dubbo.apache.org/dubbo-go/[email protected]/common/url.go:608
 +0x38
   
dubbo.apache.org/dubbo-go/v3/registry/directory.(*RegistryDirectory).toGroupInvokers.func1({0x105e3b060,
 0x14000ebb700}, {0x105fec1e0, 0x14001a03f20})
   
/Users/yangzhen/dev/go/pkg/mod/dubbo.apache.org/dubbo-go/[email protected]/registry/directory/directory.go:393
 +0xa0
   internal/sync.(*HashTrieMap[...]).iter(0x10626b3c0, 0x140012065a0, 
0x14001547b58)
   
/Users/yangzhen/dev/go/pkg/mod/golang.org/[email protected]/src/internal/sync/hashtriemap.go:512
 +0x1b4
   internal/sync.(*HashTrieMap[...]).Range(0x10626b3c0, 0x14001547b58)
   
/Users/yangzhen/dev/go/pkg/mod/golang.org/[email protected]/src/internal/sync/hashtriemap.go:495
 +0x84
   sync.(*Map).Range(0x14001b99f20, 0x14001547b58)
   
/Users/yangzhen/dev/go/pkg/mod/golang.org/[email protected]/src/sync/hashtriemap.go:115
 +0x3c
   
dubbo.apache.org/dubbo-go/v3/registry/directory.(*RegistryDirectory).toGroupInvokers(0x1400075b8c0)
   
/Users/yangzhen/dev/go/pkg/mod/dubbo.apache.org/dubbo-go/[email protected]/registry/directory/directory.go:391
 +0x148
   
dubbo.apache.org/dubbo-go/v3/registry/directory.(*RegistryDirectory).setNewInvokers(0x1400075b8c0)
   
/Users/yangzhen/dev/go/pkg/mod/dubbo.apache.org/dubbo-go/[email protected]/registry/directory/directory.go:340
 +0x24
   
dubbo.apache.org/dubbo-go/v3/registry/directory.(*RegistryDirectory).refreshInvokers(0x1400075b8c0,
 0x14001a0a2a0)
   
/Users/yangzhen/dev/go/pkg/mod/dubbo.apache.org/dubbo-go/[email protected]/registry/directory/directory.go:241
 +0x1b4
   
dubbo.apache.org/dubbo-go/v3/registry/directory.(*RegistryDirectory).Notify(0x1400075b8c0,
 0x14001a0a2a0)
   
/Users/yangzhen/dev/go/pkg/mod/dubbo.apache.org/dubbo-go/[email protected]/registry/directory/directory.go:219
 +0x44
   
dubbo.apache.org/dubbo-go/v3/registry.(*BaseRegistry).Subscribe(0x14000756ea0, 
0x14001d981c0, {0x106238250, 0x1400075b8c0})
   
/Users/yangzhen/dev/go/pkg/mod/dubbo.apache.org/dubbo-go/[email protected]/registry/base_registry.go:354
 +0x44c
   
dubbo.apache.org/dubbo-go/v3/registry/directory.(*RegistryDirectory).Subscribe.func1()
   
/Users/yangzhen/dev/go/pkg/mod/dubbo.apache.org/dubbo-go/[email protected]/registry/directory/directory.go:167
 +0x8c
   created by 
dubbo.apache.org/dubbo-go/v3/registry/directory.(*RegistryDirectory).Subscribe 
in goroutine 271980
   
/Users/yangzhen/dev/go/pkg/mod/dubbo.apache.org/dubbo-go/[email protected]/registry/directory/directory.go:165
 +0x160
   
   # 测试的 java 服务提供者代码
   git clone https://gitee.com/yangzhen94/hyperway-dubbo.git
   
   jdk11
   
   修改 resources/application.xml 中的 address 地址
   
   # 测试逻辑
   
   1. 初始化 dubbo 服务后启动协程定时 1s 后调用 config.ReferenceConfig#Destroy
   2. main 函数中启动多个协程并发调用泛化服务
   
   忽略以下日志,第一个是重新构建 ReferenceConfig 调用服务时出现的,第二个是偶发的可以捕获的 panic
   1. dubbo rpc invoke failed: Rpc cluster invoker for 
com.hyperway.rpc.client.IHelloService on consumer 10.202.161.36 use dubbo 
version 3.3.1 is now destroyed! can not invoke any more.
   2. dubbo rpc invoke panic
   
   # 代码
   
   ## dubbo/generic_api_def.go
   
   泛化服务的api结构
   
   ```go
   package dubbo
   
   import (
        "time"
   
        hessian "github.com/apache/dubbo-go-hessian2"
   )
   
   type (
        GenericApi struct {
                application   string // 所属应用
                protocol      string // 协议,必须
                interfaceName string // 接口名,必须
                version       string // 版本
                group         string // 分组
                timeout       string // 超时时间,注意要带单位,例如:5s
                retries       string // 重试次数
   
                waitForInitSecond time.Duration // 
静态注册的dubbo消费者初始化的时间,单位:秒,动态注册的api等待时间是固定值
        }
   
        Args struct {
                Method      string            // 方法名,必需
                Names       []string          // 参数名,非必需
                Types       []string          // 参数类型,必需,java类型参考:JavaType
                Values      []hessian.Object  // 参数值,必需
                Attachments map[string]string // 附加信息,非必需
        }
   )
   
   // 仅仅缓存dubbogo.yaml中consumer配置的key
   var readOnlyConsumers map[string]*GenericApi
   
   // RefId 返回api的唯一标识
   func (ga *GenericApi) RefId() string {
        return ga.group + "/" + ga.interfaceName + ":" + ga.version
   }
   
   // getConsumerApi 根据dubbogo.yaml中consumer配置的key获取消费者api
   func getConsumerApi(key string) *GenericApi {
        return readOnlyConsumers[key]
   }
   ```
   
   ## dubbo/generic_load.go
   
   加载泛化服务
   
   ```go
   package dubbo
   
   import (
        "context"
        "fmt"
        "log"
        "math/rand"
        "sync"
        "sync/atomic"
        "time"
   
        "dubbo.apache.org/dubbo-go/v3/common"
        "dubbo.apache.org/dubbo-go/v3/common/constant"
        "dubbo.apache.org/dubbo-go/v3/config"
        "dubbo.apache.org/dubbo-go/v3/config/generic"
        "dubbo.apache.org/dubbo-go/v3/protocol"
        "github.com/pkg/errors"
   )
   
   var t *rpcTransporter
   
   type (
        rpcTransporter struct {
                Application *config.ApplicationConfig         
`validate:"required" yaml:"application" json:"application,omitempty" 
property:"application"`
                Registries  map[string]*config.RegistryConfig 
`yaml:"registries" json:"registries" property:"registries"`
                Logger      *config.LoggerConfig              `yaml:"logger" 
json:"logger,omitempty" property:"logger"`
                // Consumer不参与config.loadReference
                // 获取registryIds/RequestTimeout/References
                Consumer   *config.ConsumerConfig `yaml:"consumer" 
json:"consumer" property:"consumer"`
                references *referenceCache
        }
   
        // referenceCache 缓存引用配置
        referenceCache struct {
                references map[string]*reference
                lock       *sync.RWMutex // 保证 references 的并发安全
        }
        // reference 引用配置,可以复用
        reference struct {
                lock          *sync.Mutex             // 保证 rf 的并发安全
                lastDestroyAt atomic.Int64            // 上次销毁时间
                rf            *config.ReferenceConfig // 泛化服务的引用,可以销毁
        }
   
        rpcService struct {
                *generic.GenericService
                *reference
        }
   )
   
   func (t *rpcTransporter) loadRpcService(api *GenericApi) (*rpcService, 
error) {
        ref := t.references.getReference(api.RefId())
        if ref == nil {
                ref = t.references.loadReference(api.RefId())
        }
        commonRPCService, err := ref.load(api)
        if err != nil {
                return nil, err
        }
        return &rpcService{
                GenericService: commonRPCService.(*generic.GenericService),
                reference:      ref,
        }, nil
   }
   
   // preloadRPCService 加载RPC服务
   func (t *rpcTransporter) preloadRPCService(api *GenericApi) 
(common.RPCService, error) {
        ref := t.references.getReference(api.RefId())
        if ref == nil {
                ref = t.references.loadReference(api.RefId())
        }
        load, err := ref.load(api)
        return load, err
   }
   
   // loadReference 加载引用配置
   func (rc *referenceCache) loadReference(refId string) *reference {
        rc.lock.Lock()
        defer rc.lock.Unlock()
        if r, ok := rc.references[refId]; ok {
                return r
        } else {
                r = &reference{
                        lock:          &sync.Mutex{},
                        lastDestroyAt: atomic.Int64{},
                        rf:            nil,
                }
                rc.references[refId] = r
                return r
        }
   }
   
   // load 加载泛化服务
   func (r *reference) load(api *GenericApi) (common.RPCService, error) {
        r.lock.Lock()
        defer r.lock.Unlock()
        refId := api.RefId()
   
        if r.rf != nil {
                return r.rf.GetRPCService(), nil
        }
   
        if api.protocol == "" {
                api.protocol = constant.Dubbo
        }
   
        if api.timeout == "" {
                api.timeout = t.Consumer.RequestTimeout
        }
   
        if api.retries == "" {
                api.retries = "0"
        }
   
        r.rf = config.NewReferenceConfigBuilder().
                SetInterface(api.interfaceName).
                SetGroup(api.group).
                SetVersion(api.version).
                SetRequestTimeout(api.timeout).
                SetRetries(api.retries).
                SetProtocol(api.protocol).
                SetRegistryIDs(t.Consumer.RegistryIDs...).
                SetGeneric(true).
                SetSerialization(constant.Hessian2Serialization).
                SetLoadbalance(constant.LoadBalanceKeyRandom).
                SetCluster(constant.ClusterKeyFailover).
                Build()
        rootConfig := config.GetRootConfig()
        err := r.rf.Init(rootConfig)
        if err != nil {
                return nil, err
        }
   
        // v3.2.0-rc1 refConf.GenericLoad(refId)存在bug,需要手动创建和关联相关的GenericService
        // v3.3.0 修复了这个bug,直接使用refConf.GenericLoad(refId)即可
        // refConf.GenericLoad(refId)
        // 自维护,所以手动创建和关联GenericService,而且不需要 onfig.SetConsumerService(gs)
        gs := generic.NewGenericService(refId)
        r.rf.Refer(gs)
        r.rf.Implement(gs)
        // config.SetConsumerService(gs)
   
        waitTime := api.waitForInitSecond
        if waitTime <= 0 {
                waitTime = 5 * time.Second
        }
   
        timeout, cancel := context.WithTimeout(context.Background(), waitTime)
        defer cancel()
        index := 0
   end:
        for {
                index++
                select {
                case <-timeout.Done():
                        log.Println("dubbo rpc service load timeout")
                        break end
                default:
                        b := r.recheck(r.rf.GetInvoker())
                        if b {
                                log.Printf("dubbo rpc service load success, 
recheck count: %d\n", index)
                                break end
                        }
                        time.Sleep(1 * time.Second)
                }
        }
   
        // TODO
        r.testDestroy(refId)
        return r.rf.GetRPCService(), nil
   }
   
   // recheck 检查invoker是否可用
   func (r *reference) recheck(invoker protocol.Invoker) bool {
        defer func() {
                // invoker.IsAvailable 逻辑中可能会有 panic: runtime error: invalid 
memory address or nil pointer dereference
                if r := recover(); r != nil {
                        log.Println("generic service not ready, wait for next 
check")
                }
        }()
        if !invoker.IsAvailable() {
                return false
        }
        return true
   }
   
   // getReference 从缓存中获取 reference
   func (rc *referenceCache) getReference(refId string) *reference {
        rc.lock.RLock()
        defer rc.lock.RUnlock()
        if r, ok := rc.references[refId]; ok {
                return r
        }
        return nil
   }
   
   // loadConsumer 加载消费者
   func (t *rpcTransporter) loadConsumer() error {
        readOnlyConsumers = make(map[string]*GenericApi)
        for key, referenceConfig := range t.Consumer.References {
                api := &GenericApi{
                        application:       t.Application.Name,
                        protocol:          referenceConfig.Protocol,
                        interfaceName:     referenceConfig.InterfaceName,
                        version:           referenceConfig.Version,
                        group:             referenceConfig.Group,
                        timeout:           referenceConfig.RequestTimeout,
                        retries:           referenceConfig.Retries,
                        waitForInitSecond: 5 * time.Second,
                }
                if _, err := t.preloadRPCService(api); err != nil {
                        return errors.Wrap(err, "failed to load consumers")
                }
                readOnlyConsumers[key] = api
        }
        return nil
   }
   
   // destroy 销毁泛化服务,仅作为测试时使用
   func (r *reference) destroy() {
        r.lock.Lock()
        defer r.lock.Unlock()
        if r.rf != nil {
                invoker := r.rf.GetInvoker()
                r.rf = nil
                invoker.Destroy()
        }
   }
   
   // testDestroy 测试定时销毁泛化服务,
   // Deprecated: 该方法会触发dubbo底层异步协程的panic,导致应用宕机
   func (r *reference) testDestroy(refId string) {
        go func() {
                random := rand.New(rand.NewSource(time.Now().UnixNano()))
                delay := random.Intn(6) + 10
                delay = 1
                sc := time.NewTimer(time.Duration(delay) * time.Second)
                defer sc.Stop()
                select {
                case <-sc.C:
                        fmt.Printf("[DESTROY-%s] %p\n", refId, r.rf)
                        r.destroy()
                }
        }()
   }
   ```
   
   ## dubbo/generic_invoke.go
   
   调用泛化服务
   
   ```go
   package dubbo
   
   import (
        "context"
        "runtime/debug"
        "strings"
   
        "dubbo.apache.org/dubbo-go/v3/common/constant"
        _ "dubbo.apache.org/dubbo-go/v3/imports"
        "github.com/mitchellh/mapstructure"
        "github.com/pkg/errors"
   )
   
   // ProxyInvoke 代理dubbo服务的调用,返回 T 对应的结果
   // consumerKey 消费者配置的key
   // args 请求参数
   // return *T T可能为nil
   // return error 错误信息
   // 响应结构使用包装,判断请求成功与否
   func ProxyInvoke[T any](ctx context.Context, consumerKey string, args *Args) 
(*T, error) {
   
        api := getConsumerApi(consumerKey)
   
        if api == nil {
                return nil, errors.New("failed to get consumer api for key: " + 
consumerKey)
        }
   
        if args.Attachments == nil {
                args.Attachments = make(map[string]string)
        }
   
        result, err := InvokeAndDecoded[T](ctx, api, args)
   
        if err != nil {
                return nil, err
        }
   
        if result == nil {
                return nil, nil
        }
   
        return result, nil
   }
   
   // Invoke 执行调用
   // ctx: 上下文
   // api: 泛化调用的API
   // args: 
参数,参数类型的顺序要和接口定义的顺序一致,否则会出现NoSuchMethod,也可能导致NoProvider的错误,dubbo-admin查不到元数据,但实际dubbo服务是存在的
   // traceId: 链路追踪
   // return any: 泛化调用的结果,可能为nil
   func Invoke(ctx context.Context, api *GenericApi, args *Args) (resp any, err 
error) {
   
        defer func() {
                if rec := recover(); rec != nil {
                        // 打印堆栈到标准输出
                        debug.PrintStack()
                        err = errors.Errorf("dubbo rpc invoke panic: %v\n%s", 
rec, debug.Stack())
                }
        }()
   
        var rpc *rpcService
   
        if rpc, err = t.loadRpcService(api); err != nil {
                return nil, errors.Wrap(err, "failed to load dubbo rpc service")
        }
   
        defer func() {
                if err != nil {
                        // dubbo3.3.0泛化调用会出现 session Already Closed,之后服务不可用,无法恢复
                        // 当出现该错误时,销毁引用,下次重新创建
                        if strings.HasPrefix(err.Error(), "session Already 
Closed") {
                                rpc.destroy()
                        }
                        err = errors.Wrap(err, "dubbo rpc invoke failed")
                }
        }()
   
        dubboCtx := dealAttachments(ctx, args)
   
        // 泛化调用的接口可能返回nil,例如定义的返回值是void或者返回null
        // 传递的参数类型和接口定义的参数类型不匹配时报NoSuchMethod的异常
        resp, err = rpc.Invoke(
                dubboCtx, args.Method, args.Types, args.Values,
        )
   
        if err != nil {
                return nil, err
        }
        return resp, nil
   }
   
   func InvokeAndDecoded[T any](ctx context.Context, api *GenericApi, args 
*Args) (*T, error) {
        resp, err := Invoke(ctx, api, args)
        if err != nil {
                return nil, err
        }
   
        if resp == nil {
                return nil, nil
        }
   
        var o T
   
        // 当结构体定义的类型和返回数据的类型不匹配时,会报错,例如定义int但返回string
        // 不支持json字符串的解码
        // 不支持基本数据类型的解码
        // 当resp为nil时,Decode不会报错
        err = mapstructure.Decode(resp, &o)
        if err != nil {
                return nil, errors.Wrap(err, "response decode failed")
        }
        return &o, nil
   }
   
   // dealAttachments 处理dubbo的附加参数
   func dealAttachments(ctx context.Context, args *Args) context.Context {
        if args.Attachments == nil {
                args.Attachments = map[string]string{}
        }
        return context.WithValue(ctx, constant.AttachmentKey, args.Attachments)
   }
   ```
   ## dubbo/init.go
   
   初始化dubbo
   
   ```go
   package dubbo
   
   import (
        "os"
        "strings"
        "sync"
   
        "dubbo.apache.org/dubbo-go/v3/common/constant"
        "dubbo.apache.org/dubbo-go/v3/config"
        "github.com/knadh/koanf"
        "github.com/knadh/koanf/parsers/json"
        "github.com/knadh/koanf/parsers/toml"
        "github.com/knadh/koanf/parsers/yaml"
        "github.com/knadh/koanf/providers/rawbytes"
        "github.com/magiconair/properties"
        "github.com/pkg/errors"
   )
   
   var (
        once sync.Once
   )
   
   // Init 
使用官网的dubbogo.yaml配置文件,但目前不需要环境变量DUBBO_GO_CONFIG_PATH=./conf/dubbogo.yaml
   func Init() {
        once.Do(func() {
                k, err := LoadKoanf("dubbogo.yaml", ".", "yaml")
                if err != nil {
                        panic(err)
                }
   
                t = &rpcTransporter{
                        Consumer:    &config.ConsumerConfig{},
                        Application: &config.ApplicationConfig{},
                        Registries:  make(map[string]*config.RegistryConfig),
                        Logger:      &config.LoggerConfig{},
                        references: &referenceCache{
                                references: make(map[string]*reference),
                                lock:       &sync.RWMutex{},
                        },
                }
   
                err = k.UnmarshalWithConf(constant.Dubbo, t, 
koanf.UnmarshalConf{Tag: "yaml"})
                if err != nil {
                        panic(err)
                }
   
                rootConfig := config.NewRootConfigBuilder().
                        SetApplication(t.Application).
                        SetRegistries(t.Registries).
                        SetLogger(t.Logger).
                        Build()
                if err = config.Load(config.WithRootConfig(rootConfig)); err != 
nil {
                        panic(err)
                }
   
                err = t.loadConsumer()
                if err != nil {
                        panic(err)
                }
        })
   }
   
   // LoadKoanf config from file
   func LoadKoanf(filepath, delim string, suffix string) (*koanf.Koanf, error) {
   
        bytes, err := os.ReadFile(filepath)
        if err != nil {
                return nil, err
        }
   
        k := koanf.New(delim)
   
        switch suffix {
        case "yaml", "yml":
                err = k.Load(rawbytes.Provider(bytes), yaml.Parser())
        case "json":
                err = k.Load(rawbytes.Provider(bytes), json.Parser())
        case "toml":
                err = k.Load(rawbytes.Provider(bytes), toml.Parser())
        case "properties":
                err = k.Load(rawbytes.Provider(bytes), Parser())
        default:
                err = errors.Errorf("no support %s file suffix", suffix)
        }
        if err != nil {
                return nil, err
        }
        return k, nil
   }
   
   type Properties struct{}
   
   func Parser() *Properties {
        return &Properties{}
   }
   
   // Unmarshal parses the given properties bytes.
   func (p *Properties) Unmarshal(b []byte) (map[string]any, error) {
        out := make(map[string]any)
        if load, err := properties.Load(b, properties.UTF8); err != nil {
                return nil, err
        } else {
                // see viper#unmarshalReader
                for _, key := range load.Keys() {
                        value, _ := load.Get(key)
                        // recursively build nested maps
                        path := strings.Split(key, ".")
                        lastKey := path[len(path)-1]
                        deepestMap := deepSearch(out, path[0:len(path)-1])
                        // set innermost value
                        deepestMap[lastKey] = value
                }
                return out, nil
        }
   }
   
   func (p *Properties) Marshal(o map[string]any) ([]byte, error) {
        return nil, nil
   }
   
   func deepSearch(m map[string]any, path []string) map[string]any {
        for _, k := range path {
                m2, ok := m[k]
                if !ok {
                        // intermediate key does not exist
                        // => create it and continue from there
                        m3 := make(map[string]any)
                        m[k] = m3
                        m = m3
                        continue
                }
                m3, ok := m2.(map[string]any)
                if !ok {
                        // intermediate key is a value
                        // => replace with a new map
                        m3 = make(map[string]any)
                        m[k] = m3
                }
                // continue search from here
                m = m3
        }
        return m
   }
   ```
   
   ## mock/mock.go
   
   ```go
   package mock
   
   import (
        "context"
        "dubbo-go-demo/dubbo"
        "fmt"
   
        hessian "github.com/apache/dubbo-go-hessian2"
   )
   
   type (
        ResultVO struct {
                Value *UserVO
        }
        // UserVO 用户信息
        UserVO struct {
                ID   int64  `json:"id"`
                Name string `json:"name"`
        }
   )
   
   // GetUserVOList 获取UserVO列表数据
   func GetUserVOList(ctx context.Context) {
        args := &dubbo.Args{
                Method: "helloList",
                Types: []string{
                        "java.util.List",
                },
                Values: []hessian.Object{
                        []map[string]interface{}{
                                {
                                        "id":   123,
                                        "name": "user-123",
                                }, {
                                        "id":   456,
                                        "name": "user-456",
                                },
                        },
                },
        }
   
        _, err := dubbo.ProxyInvoke[[]UserVO](ctx, "demo-rpc", args)
        if err != nil {
                fmt.Printf("[ERROR-USER-VO-LIST]: %v\n", err)
        } else {
                //fmt.Printf("[USER-VO-LIST]: %+v\n", result)
        }
   }
   
   // GetUserVO 获取UserVO数据
   func GetUserVO(ctx context.Context) {
        args := &dubbo.Args{
                Method: "helloUser",
                Types:  []string{"com.hyperway.rpc.client.User"},
                Values: []hessian.Object{
                        map[string]any{
                                "id":   123,
                                "name": "user-123",
                        },
                },
        }
   
        _, err := dubbo.ProxyInvoke[ResultVO](ctx, "demo-rpc", args)
        if err != nil {
                fmt.Printf("[ERROR-USER-VO]: %v\n", err)
                //} else if result != nil {
                //      fmt.Printf("[USER-VO]: %+v\n", result.Value)
        }
   }
   ```
   
   ## main.go
   
   ```go
   package main
   
   import (
        "context"
        "dubbo-go-demo/dubbo"
        "dubbo-go-demo/mock"
        "math/rand"
        "time"
   )
   
   func main() {
   
        dubbo.Init()
   
        time.Sleep(3 * time.Second)
        minMs, maxMs := 100, 1000
        // 5个goroutine并发调用dubboCall,每次调用休眠100ms,goroutine要一直运行
        for i := 0; i < 150; i++ {
                go func() {
                        random := 
rand.New(rand.NewSource(time.Now().UnixNano()))
                        for {
                                // rand.Intn(n) 会返回 [0, n) 的整数,所以这里要加上 min
                                ms := random.Intn(maxMs-minMs+1) + minMs
                                //go mock.GetInteger(context.Background())
                                go mock.GetUserVO(context.Background())
                                go mock.GetUserVOList(context.Background())
                                time.Sleep(time.Duration(ms) * time.Millisecond)
                        }
                }()
        }
   
        select {
        case <-time.After(1 * time.Hour):
        }
   }
   ```
   
   ## dubbogo.yaml
   ```yaml
   dubbo:
     application:
       organization: "demo"
       name: "demo"
       owner: "demo"
     registries:
       zk:
         protocol: "zookeeper"
         timeout: "10s"
   #      address: "127.0.0.1:2181"
         address: "10.202.244.20:32181"
         registry-type: "interface"
     logger:
       level: "warn"
       format: "json"
       driver: "zap"
       appender: "file"
       file:
         name: "dubbo.log"
         max-size: 5
         max-backups: 3
         max-age: 3
         compress: false
     consumer:
       registry-ids: [ "zk" ]
       request-timeout: "16s"
       references:
         demo-rpc:
           protocol: "dubbo"
           interface: "com.hyperway.rpc.client.IHelloService"
           timeout: "6s"
           retries: "2"
           registry: "zk"
   ```
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to