func (e *etcdV3ServiceDiscovery) AddListener(listener registry.ServiceInstancesChangedListener) error {
	for _, t := range listener.GetServiceNames().Values() {
		err := e.registerServiceInstanceListener(t.(string), listener)
		if err != nil {
			return err
		}
		err = e.registerServiceWatcher(t.(string))
		if err != nil {
			return err
		}
	}
	return nil
}
----------------- event ----------------------
AddListener adds a new ServiceInstancesChangedListenerImpl
see addServiceInstancesChangedListener in Java
package etcdv3
import (
	"fmt"
	"strings"
	"sync"
	"time"
)
import (
	gxset "github.com/dubbogo/gost/container/set"
	gxetcd "github.com/dubbogo/gost/database/kv/etcd/v3"
	gxpage "github.com/dubbogo/gost/hash/page"
	"github.com/hashicorp/vault/sdk/helper/jsonutil"
)
import (
	"dubbo.apache.org/dubbo-go/v3/common/constant"
	"dubbo.apache.org/dubbo-go/v3/common/extension"
	"dubbo.apache.org/dubbo-go/v3/common/logger"
	"dubbo.apache.org/dubbo-go/v3/config"
	"dubbo.apache.org/dubbo-go/v3/registry"
	"dubbo.apache.org/dubbo-go/v3/remoting"
	"dubbo.apache.org/dubbo-go/v3/remoting/etcdv3"
)
const (
	ROOT = "/services"
)
var initLock sync.Mutex
func init() {
	extension.SetServiceDiscovery(constant.ETCDV3_KEY, newEtcdV3ServiceDiscovery)
}
type etcdV3ServiceDiscovery struct {
	descriptor string
	client *gxetcd.Client
	serviceInstance *registry.ServiceInstance
	services *gxset.HashSet
	childListenerMap    map[string]*etcdv3.EventListener
	instanceListenerMap map[string]*gxset.HashSet
}
func (e *etcdV3ServiceDiscovery) String() string {
	return e.descriptor
}
func (e *etcdV3ServiceDiscovery) Destroy() error {
	if e.client != nil {
		e.client.Close()
	}
	return nil
}
func (e *etcdV3ServiceDiscovery) Register(instance registry.ServiceInstance) error {
	e.serviceInstance = &instance
	path := toPath(instance)
	if nil != e.client {
		ins, err := jsonutil.EncodeJSON(instance)
		if err == nil {
			err = e.client.RegisterTemp(path, string(ins))
			if err != nil {
				logger.Errorf("cannot register the instance: %s", string(ins), err)
			} else {
				e.services.Add(instance.GetServiceName())
			}
		}
	}
	return nil
}
func (e *etcdV3ServiceDiscovery) Update(instance registry.ServiceInstance) error {
	path := toPath(instance)
	if nil != e.client {
		ins, err := jsonutil.EncodeJSON(instance)
		if err == nil {
			if err = e.client.RegisterTemp(path, string(ins)); err != nil {
				logger.Warnf("etcdV3ServiceDiscovery.client.RegisterTemp(path:%v, instance:%v) = error:%v",
					path, string(ins), err)
			}
			e.services.Add(instance.GetServiceName())
		}
	}
	return nil
}
func (e *etcdV3ServiceDiscovery) Unregister(instance registry.ServiceInstance) error {
	path := toPath(instance)
	if nil != e.client {
		err := e.client.Delete(path)
		e.services.Remove(instance.GetServiceName())
		e.serviceInstance = nil
		return err
	}
	return nil
}
func (e *etcdV3ServiceDiscovery) GetDefaultPageSize() int {
	return registry.DefaultPageSize
}
func (e *etcdV3ServiceDiscovery) GetServices() *gxset.HashSet {
	return e.services
}
func (e *etcdV3ServiceDiscovery) GetInstances(serviceName string) []registry.ServiceInstance {
	if nil != e.client {
		_, vList, err := e.client.GetChildrenKVList(toParentPath(serviceName))
		if nil == err {
			serviceInstances := make([]registry.ServiceInstance, 0, len(vList))
			for _, v := range vList {
				instance := &registry.DefaultServiceInstance{}
				err = jsonutil.DecodeJSON([]byte(v), &instance)
				if nil == err {
					serviceInstances = append(serviceInstances, instance)
				}
			}
			return serviceInstances
		}
		logger.Infof("could not getChildrenKVList the err is:%v", err)
	}
	return make([]registry.ServiceInstance, 0)
}
func (e *etcdV3ServiceDiscovery) GetInstancesByPage(serviceName string, offset int, pageSize int) gxpage.Pager {
	all := e.GetInstances(serviceName)
	res := make([]interface{}, 0, pageSize)
	for i := offset; i < len(all) && i < offset+pageSize; i++ {
		res = append(res, all[i])
	}
	return gxpage.NewPage(offset, pageSize, res, len(all))
}
func (e *etcdV3ServiceDiscovery) GetHealthyInstancesByPage(serviceName string, offset int, pageSize int, healthy bool) gxpage.Pager {
	all := e.GetInstances(serviceName)
	res := make([]interface{}, 0, pageSize)
	var (
		i     = offset
		count = 0
	)
	for i < len(all) && count < pageSize {
		ins := all[i]
		if ins.IsHealthy() == healthy {
			res = append(res, all[i])
			count++
		}
		i++
	}
	return gxpage.NewPage(offset, pageSize, res, len(all))
}
func (e *etcdV3ServiceDiscovery) GetRequestInstances(serviceNames []string, offset int, requestedSize int) map[string]gxpage.Pager {
	res := make(map[string]gxpage.Pager, len(serviceNames))
	for _, name := range serviceNames {
		res[name] = e.GetInstancesByPage(name, offset, requestedSize)
	}
	return res
}