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

xiaoliu pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/dubbo-go-pixiu.git


The following commit(s) were added to refs/heads/develop by this push:
     new 29f3232  make Pixiu Admin config management finer-grained (#171)
29f3232 is described below

commit 29f3232fab2d38ec144f36f7b91b50d17953c843
Author: randy <[email protected]>
AuthorDate: Fri Jun 25 09:04:09 2021 +0800

    make Pixiu Admin config management finer-grained (#171)
    
    * init
    
    * update
    
    * add gin
    
    * baseinfo and resources
    
    * api complete
    
    * import
    
    * import
    
    * update
    
    * update
    
    * update
    
    * update
    
    * replace path with id
    
    * update
    
    * update
    
    * update
    
    * update
    
    * comment
    
    * use go 1.15 run ci to fix ci problem
    
    * use gost zk
    
    * add lock
    
    * modify import and chinese comment
    
    * split func and bugfix
    
    * fix go.mod go.sum
    
    * fix go.mod go.sum
    
    * fix go.mod go.sum
    
    * import location
---
 .github/workflows/github-actions.yml |   2 +-
 .travis.yml                          |   2 +-
 cmd/admin/admin.go                   | 193 ---------------
 cmd/pixiu/control.go                 |   2 +-
 configs/admin_config.yaml            |  23 --
 configs/api_config.yaml              |   1 +
 go.mod                               |  13 +-
 go.sum                               |  80 ++----
 pkg/config/api_config.go             | 332 +++++++++++++++++++++----
 pkg/pixiu/pixiu_start.go             |   4 +-
 pkg/registry/zookeeper.go            |   6 +-
 pkg/registry/zookeeper_test.go       |  11 +-
 pkg/remoting/etcd3/client.go         | 458 -----------------------------------
 pkg/remoting/etcd3/client_test.go    | 399 ------------------------------
 pkg/router/route.go                  |  23 ++
 pkg/service/api/discovery_service.go | 197 +++++++++++----
 pkg/service/discovery_service.go     |   4 +-
 samples/admin/sample.md              |  13 +-
 18 files changed, 519 insertions(+), 1244 deletions(-)

diff --git a/.github/workflows/github-actions.yml 
b/.github/workflows/github-actions.yml
index 8b64eb0..f0cff48 100644
--- a/.github/workflows/github-actions.yml
+++ b/.github/workflows/github-actions.yml
@@ -35,7 +35,7 @@ jobs:
       # If you want to matrix build , you can append the following list.
       matrix:
         go_version:
-          - 1.14
+          - 1.15
         os:
           - ubuntu-latest
 
diff --git a/.travis.yml b/.travis.yml
index 2fbb7a8..118731a 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -19,7 +19,7 @@
 language: go
 
 go:
-  - "1.14"
+  - "1.15"
 env:
   - GO111MODULE=on
 
diff --git a/cmd/admin/admin.go b/cmd/admin/admin.go
deleted file mode 100644
index 5ac09c4..0000000
--- a/cmd/admin/admin.go
+++ /dev/null
@@ -1,193 +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 main
-
-import (
-       "io/ioutil"
-       "net/http"
-       "os"
-       "strconv"
-       "strings"
-       "time"
-)
-
-import (
-       fc "github.com/dubbogo/dubbo-go-pixiu-filter/pkg/api/config"
-       perrors "github.com/pkg/errors"
-       "github.com/urfave/cli"
-)
-
-import (
-       "github.com/apache/dubbo-go-pixiu/pkg/common/yaml"
-       "github.com/apache/dubbo-go-pixiu/pkg/logger"
-       etcdv3 "github.com/apache/dubbo-go-pixiu/pkg/remoting/etcd3"
-)
-
-// AdminBootstrap admin bootstrap config
-type AdminBootstrap struct {
-       Server     ServerConfig `yaml:"server" json:"server" 
mapstructure:"server"`
-       EtcdConfig EtcdConfig   `yaml:"etcd" json:"etcd" mapstructure:"etcd"`
-}
-
-// ServerConfig admin http server config
-type ServerConfig struct {
-       Address string `yaml:"address" json:"address" mapstructure:"address"`
-}
-
-// EtcdConfig admin etcd client config
-type EtcdConfig struct {
-       Address string `yaml:"address" json:"admin" mapstructure:"admin"`
-       Path    string `yaml:"path" json:"path" mapstructure:"path"`
-}
-
-var (
-       cmdStart = cli.Command{
-               Name:  "start",
-               Usage: "start dubbogo pixiu admin",
-               Flags: []cli.Flag{
-                       cli.StringFlag{
-                               Name:   "config, c",
-                               Usage:  "Load configuration from `FILE`",
-                               EnvVar: "PROXY_ADMIN_CONFIG",
-                               Value:  "configs/admin_config.yaml",
-                       },
-               },
-               Action: func(c *cli.Context) error {
-                       configPath := c.String("config")
-                       _, err := LoadAPIConfigFromFile(configPath)
-                       if err != nil {
-                               logger.Errorf("load admin config  error:%+v", 
err)
-                               return err
-                       }
-                       Start()
-                       return nil
-               },
-       }
-
-       cmdStop = cli.Command{
-               Name:  "stop",
-               Usage: "stop dubbogo pixiu admin",
-               Action: func(c *cli.Context) error {
-                       return nil
-               },
-       }
-)
-
-// Version admin version
-const Version = "0.1.0"
-
-func newAdminApp(startCmd *cli.Command) *cli.App {
-       app := cli.NewApp()
-       app.Name = "dubbogo pixiu admin"
-       app.Version = Version
-       app.Compiled = time.Now()
-       app.Copyright = "(c) " + strconv.Itoa(time.Now().Year()) + " Dubbogo"
-       app.Usage = "Dubbogo pixiu admin"
-       app.Flags = cmdStart.Flags
-
-       // commands
-       app.Commands = []cli.Command{
-               cmdStart,
-               cmdStop,
-       }
-
-       // action
-       app.Action = func(c *cli.Context) error {
-               if c.NumFlags() == 0 {
-                       return cli.ShowAppHelp(c)
-               }
-               return startCmd.Action.(func(c *cli.Context) error)(c)
-       }
-
-       return app
-}
-
-// LoadAPIConfigFromFile load config from file
-func LoadAPIConfigFromFile(path string) (*AdminBootstrap, error) {
-       if len(path) == 0 {
-               return nil, perrors.Errorf("Config file not specified")
-       }
-       adminBootstrap := &AdminBootstrap{}
-       err := yaml.UnmarshalYMLConfig(path, adminBootstrap)
-       if err != nil {
-               return nil, perrors.Errorf("unmarshalYmlConfig error %v", 
perrors.WithStack(err))
-       }
-       bootstrap = adminBootstrap
-       return adminBootstrap, nil
-}
-
-func main() {
-       app := newAdminApp(&cmdStart)
-       // ignore error so we don't exit non-zero and break gfmrun README 
example tests
-       _ = app.Run(os.Args)
-}
-
-var (
-       client    *etcdv3.Client
-       bootstrap *AdminBootstrap
-)
-
-// Start start init etcd client and start admin http server
-func Start() {
-       client = etcdv3.NewConfigClient(
-               etcdv3.WithName(etcdv3.RegistryETCDV3Client),
-               etcdv3.WithTimeout(10*time.Second),
-               
etcdv3.WithEndpoints(strings.Split(bootstrap.EtcdConfig.Address, ",")...),
-       )
-       defer client.Close()
-
-       http.HandleFunc("/config/api", GetAPIConfig)
-       http.HandleFunc("/config/api/set", SetAPIConfig)
-
-       http.ListenAndServe(bootstrap.Server.Address, nil)
-}
-
-// GetAPIConfig handle get api config http request
-func GetAPIConfig(w http.ResponseWriter, req *http.Request) {
-       config, err := client.Get(bootstrap.EtcdConfig.Path)
-       if err != nil {
-               logger.Errorf("GetAPIConfig err, %v\n", err)
-               w.Write([]byte("Error"))
-       }
-       w.Write([]byte(config))
-}
-
-// SetAPIConfig handle modify api config http request
-func SetAPIConfig(w http.ResponseWriter, req *http.Request) {
-       body, err := ioutil.ReadAll(req.Body)
-       if err != nil {
-               logger.Errorf("read body err, %v\n", err)
-               return
-       }
-       // validate the api config
-       apiConf := &fc.APIConfig{}
-       err = yaml.UnmarshalYML([]byte(body), apiConf)
-
-       if err != nil {
-               logger.Warnf("read body err, %v\n", err)
-               http.Error(w, err.Error(), http.StatusBadRequest)
-               return
-       }
-
-       setErr := client.Update(bootstrap.EtcdConfig.Path, string(body))
-       if setErr != nil {
-               logger.Warnf("update etcd error, %v\n", err)
-               w.Write([]byte(setErr.Error()))
-       }
-       w.Write([]byte("Success"))
-}
diff --git a/cmd/pixiu/control.go b/cmd/pixiu/control.go
index 25e72f9..c07b70f 100644
--- a/cmd/pixiu/control.go
+++ b/cmd/pixiu/control.go
@@ -92,7 +92,7 @@ var (
                        initFromRemote := false
                        if bootstrap.GetAPIMetaConfig() != nil {
                                if _, err := 
config.LoadAPIConfig(bootstrap.GetAPIMetaConfig()); err != nil {
-                                       logger.Errorf("load api config from 
etcd error:%+v", err)
+                                       logger.Warnf("load api config from etcd 
error:%+v", err)
                                } else {
                                        initFromRemote = true
                                }
diff --git a/configs/admin_config.yaml b/configs/admin_config.yaml
deleted file mode 100644
index 8972366..0000000
--- a/configs/admin_config.yaml
+++ /dev/null
@@ -1,23 +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.
-#
-server:
-  address: 127.0.0.1:8080
-etcd:
-  address: 127.0.0.1:2379
-  path: /pixiu/config/api
diff --git a/configs/api_config.yaml b/configs/api_config.yaml
index 7b0f191..7650eae 100644
--- a/configs/api_config.yaml
+++ b/configs/api_config.yaml
@@ -22,6 +22,7 @@ resources:
   - path: '/api/v1/test-dubbo/user'
     type: restful
     description: user
+    timeout: 100ms
     plugins:
       pre:
         pluginNames:
diff --git a/go.mod b/go.mod
index af8467d..c1f0aea 100644
--- a/go.mod
+++ b/go.mod
@@ -6,13 +6,13 @@ require (
        github.com/alibaba/sentinel-golang v1.0.2
        github.com/apache/dubbo-go v1.5.7-rc1
        github.com/apache/dubbo-go-hessian2 v1.9.2
-       github.com/coreos/etcd v3.3.25+incompatible
        github.com/dubbogo/dubbo-go-pixiu-filter 
v0.1.4-0.20210613012702-8488bf80772c
        github.com/dubbogo/go-zookeeper v1.0.3
-       github.com/dubbogo/gost v1.11.7
+       github.com/dubbogo/gost v1.11.8
        github.com/emirpasic/gods v1.12.0
        github.com/ghodss/yaml v1.0.1-0.20190212211648-25d852aebe32
        github.com/goinggo/mapstructure v0.0.0-20140717182941-194205d9b4a9
+       github.com/golang/protobuf v1.5.2 // indirect
        github.com/hashicorp/consul/api v1.5.0
        github.com/pkg/errors v0.9.1
        github.com/shirou/gopsutil v3.21.3+incompatible // indirect
@@ -20,12 +20,15 @@ require (
        github.com/stretchr/testify v1.7.0
        github.com/tklauser/go-sysconf v0.3.5 // indirect
        github.com/urfave/cli v1.22.4
+       go.etcd.io/etcd v0.0.0-20200402134248-51bdeb39e698
        go.uber.org/zap v1.16.0
-       google.golang.org/grpc v1.33.1
+       golang.org/x/crypto v0.0.0-20210415154028-4f45737414dc // indirect
+       golang.org/x/sys v0.0.0-20210415045647-66c3f260301c // indirect
+       golang.org/x/text v0.3.6 // indirect
        gopkg.in/yaml.v2 v2.4.0
 )
 
 replace (
-       github.com/envoyproxy/go-control-plane => 
github.com/envoyproxy/go-control-plane v0.8.0
-       google.golang.org/grpc v1.33.1 => google.golang.org/grpc v1.26.0
+       github.com/envoyproxy/go-control-plane 
v0.9.1-0.20191026205805-5f8ba28d4473 => github.com/envoyproxy/go-control-plane 
v0.8.0
+       google.golang.org/grpc => google.golang.org/grpc v1.26.0
 )
diff --git a/go.sum b/go.sum
index d2b7690..532fd1e 100644
--- a/go.sum
+++ b/go.sum
@@ -1,4 +1,3 @@
-cloud.google.com/go v0.26.0/go.mod 
h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
 cloud.google.com/go v0.34.0/go.mod 
h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
 cloud.google.com/go v0.38.0/go.mod 
h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
 cloud.google.com/go v0.44.1/go.mod 
h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
@@ -84,7 +83,6 @@ github.com/apache/dubbo-getty v1.4.3 
h1:PCKpryDasKOxwT5MBC6MIMO+0NLOaHF6Xco9YXQw
 github.com/apache/dubbo-getty v1.4.3/go.mod 
h1:ansXgKxxyhCOiQL29nO5ce1MDcEKmCyZuNR9oMs3hek=
 github.com/apache/dubbo-go v1.5.7-rc1 
h1:PBK/Ox35L8QH85eBmHAqsg/EL2SnO+xwcCw67lQ4z4Q=
 github.com/apache/dubbo-go v1.5.7-rc1/go.mod 
h1:wLJvPWbnrf6/bhoohBT404QK4t2JoNcckMw+/fJ9P+c=
-github.com/apache/dubbo-go-hessian2 v1.9.1 
h1:ceSsU/9z/gv3hzUpl8GceEhQvF3i0BionfdHUGMmjHU=
 github.com/apache/dubbo-go-hessian2 v1.9.1/go.mod 
h1:xQUjE7F8PX49nm80kChFvepA/AvqAZ0oh/UaB6+6pBE=
 github.com/apache/dubbo-go-hessian2 v1.9.2 
h1:XuI8KvENSfKiAhiCBS4RNihmQDoPNmGWKT3gTui0p9A=
 github.com/apache/dubbo-go-hessian2 v1.9.2/go.mod 
h1:xQUjE7F8PX49nm80kChFvepA/AvqAZ0oh/UaB6+6pBE=
@@ -127,20 +125,16 @@ github.com/circonus-labs/circonus-gometrics 
v2.3.1+incompatible/go.mod h1:nmEj6D
 github.com/circonus-labs/circonusllhist v0.1.3 
h1:TJH+oke8D16535+jHExHj4nQvzlZrj7ug5D7I/orNUA=
 github.com/circonus-labs/circonusllhist v0.1.3/go.mod 
h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I=
 github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod 
h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE=
-github.com/client9/misspell v0.3.4/go.mod 
h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
-github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f 
h1:WBZRG4aNOuI15bLRrCgN8fCq8E5Xuty6jGbmSNEvSsU=
-github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod 
h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
 github.com/cockroachdb/apd v1.1.0/go.mod 
h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ=
+github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa 
h1:OaNxuTZr7kxeODyLWsRMC+OD03aFUH+mW6r2d+MWa5Y=
 github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod 
h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8=
 github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod 
h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI=
 github.com/coredns/coredns v1.1.2 
h1:bAFHrSsBeTeRG5W3Nf2su3lUGw7Npw2UKeCJm/3A638=
 github.com/coredns/coredns v1.1.2/go.mod 
h1:zASH/MVDgR6XZTbxvOnsZfffS+31vg6Ackf/wo1+AM0=
 github.com/coreos/bbolt v1.3.2/go.mod 
h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
-github.com/coreos/bbolt v1.3.3 h1:n6AiVyVRKQFNb6mJlwESEvvLoDyiTzXX7ORAUlkeBdY=
 github.com/coreos/bbolt v1.3.3/go.mod 
h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
 github.com/coreos/etcd v3.3.10+incompatible/go.mod 
h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
 github.com/coreos/etcd v3.3.13+incompatible/go.mod 
h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
-github.com/coreos/etcd v3.3.25+incompatible 
h1:0GQEw6h3YnuOVdtwygkIfJ+Omx0tZ8/QkVyXI4LkbeY=
 github.com/coreos/etcd v3.3.25+incompatible/go.mod 
h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
 github.com/coreos/go-etcd v2.0.0+incompatible/go.mod 
h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
 github.com/coreos/go-oidc v2.1.0+incompatible 
h1:sdJrfw8akMnCuUlaZU3tE/uYXFgfqom8DBE9so9EBsM=
@@ -152,9 +146,9 @@ github.com/coreos/go-systemd 
v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7
 github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod 
h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
 github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf 
h1:iW4rZ826su+pqaw19uhpSCzhj44qo35pNgKFGqzDKkU=
 github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf/go.mod 
h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
+github.com/coreos/go-systemd/v22 v22.0.0 
h1:XJIw/+VlJ+87J+doOxznsAWIdmWuViOVhkQamW5YV28=
 github.com/coreos/go-systemd/v22 v22.0.0/go.mod 
h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk=
 github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod 
h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
-github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f 
h1:lBNOc5arjvs8E5mO2tbpBpLoyyu8B6e44T7hJy6potg=
 github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod 
h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
 github.com/cpuguy83/go-md2man v1.0.10 
h1:BSKMNlYxDvnunlTymqtgONjNnaRV1sTpcovwwjF22jk=
 github.com/cpuguy83/go-md2man v1.0.10/go.mod 
h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
@@ -183,18 +177,15 @@ github.com/dnaeon/go-vcr v1.0.1/go.mod 
h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyG
 github.com/docker/go-connections v0.3.0 
h1:3lOnM9cSzgGwx8VfK/NGOW5fLQ0GjIlCkaktF+n1M6o=
 github.com/docker/go-connections v0.3.0/go.mod 
h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
 github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod 
h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM=
-github.com/dubbogo/dubbo-go-pixiu-filter v0.1.4-0.20210427062645-0bec837d429e 
h1:0d1cHanEYqLu/WRwvTe4Y2zeCeeiimVqFKTL5QXAxAs=
-github.com/dubbogo/dubbo-go-pixiu-filter 
v0.1.4-0.20210427062645-0bec837d429e/go.mod 
h1:d6SDK5BHl/QCvg84BN+g6LZS9QzVqnI2+yw0NBu0uac=
 github.com/dubbogo/dubbo-go-pixiu-filter v0.1.4-0.20210613012702-8488bf80772c 
h1:Hff+hNfuM7lx01sJy0ZmEZELNGZkNEgU8WQOz8D4Tno=
 github.com/dubbogo/dubbo-go-pixiu-filter 
v0.1.4-0.20210613012702-8488bf80772c/go.mod 
h1:o0tgVjbQyVxVq27Av9VvB5ZBv6tk4ypfIPkhfTLfmuw=
 github.com/dubbogo/go-zookeeper v1.0.3 
h1:UkuY+rBsxdT7Bs63QAzp9z7XqQ53W1j8E5rwl83me8g=
 github.com/dubbogo/go-zookeeper v1.0.3/go.mod 
h1:fn6n2CAEer3novYgk9ULLwAjuV8/g4DdC2ENwRb6E+c=
 github.com/dubbogo/gost v1.9.0/go.mod 
h1:pPTjVyoJan3aPxBPNUX0ADkXjPibLo+/Ib0/fADXSG8=
 github.com/dubbogo/gost v1.10.1/go.mod 
h1:+mQGS51XQEUWZP2JeGZTxJwipjRKtJO7Tr+FOg+72rI=
-github.com/dubbogo/gost v1.11.2 h1:NanyHmvzE1HrgI2T9H/jE/N1wkxFEj+IbM1A4RT9H7Q=
 github.com/dubbogo/gost v1.11.2/go.mod 
h1:3QQEj50QOhkWTERT785YZ5ZxIRGNdR11FCLP7FzHsMc=
-github.com/dubbogo/gost v1.11.7 h1:mS2nuUOhOQmHSPnbs/94GakMigcKhzRr0TFLreiqNyo=
-github.com/dubbogo/gost v1.11.7/go.mod 
h1:2nB8jSrxVPwW5DBsRu3FZQH1+Ual3wnRHwFqjG9+4PY=
+github.com/dubbogo/gost v1.11.8 h1:OPTG4qIyNQ949GbdgHvpvYiVNno/X/YBozOVBLuNkS4=
+github.com/dubbogo/gost v1.11.8/go.mod 
h1:2nB8jSrxVPwW5DBsRu3FZQH1+Ual3wnRHwFqjG9+4PY=
 github.com/dubbogo/jsonparser v1.0.1/go.mod 
h1:tYAtpctvSP/tWw4MeelsowSPgXQRVHHWbqL6ynps8jU=
 github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod 
h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
 github.com/dustin/go-humanize v1.0.0 
h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo=
@@ -300,8 +291,10 @@ github.com/golang/protobuf v1.4.0-rc.2/go.mod 
h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrU
 github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod 
h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
 github.com/golang/protobuf v1.4.0/go.mod 
h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
 github.com/golang/protobuf v1.4.2/go.mod 
h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
-github.com/golang/protobuf v1.4.3 
h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM=
 github.com/golang/protobuf v1.4.3/go.mod 
h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
+github.com/golang/protobuf v1.5.0/go.mod 
h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
+github.com/golang/protobuf v1.5.2 
h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
+github.com/golang/protobuf v1.5.2/go.mod 
h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
 github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod 
h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
 github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4=
 github.com/golang/snappy v0.0.1/go.mod 
h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
@@ -311,8 +304,9 @@ github.com/google/btree v1.0.0/go.mod 
h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ
 github.com/google/go-cmp v0.2.0/go.mod 
h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
 github.com/google/go-cmp v0.3.0/go.mod 
h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
 github.com/google/go-cmp v0.3.1/go.mod 
h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
-github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4=
 github.com/google/go-cmp v0.4.0/go.mod 
h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
+github.com/google/go-cmp v0.5.5/go.mod 
h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
 github.com/google/go-querystring v0.0.0-20170111101155-53e6ce116135/go.mod 
h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
 github.com/google/go-querystring v1.0.0 
h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk=
 github.com/google/go-querystring v1.0.0/go.mod 
h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
@@ -326,7 +320,6 @@ github.com/google/renameio v0.1.0/go.mod 
h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm4
 github.com/google/tcpproxy v0.0.0-20180808230851-dfa16c61dad2 
h1:AtvtonGEH/fZK0XPNNBdB6swgy7Iudfx88wzyIpwqJ8=
 github.com/google/tcpproxy v0.0.0-20180808230851-dfa16c61dad2/go.mod 
h1:DavVbd41y+b7ukKDmlnPR4nGYmkWXR6vHUkjQNiHPBs=
 github.com/google/uuid v1.0.0/go.mod 
h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
-github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
 github.com/google/uuid v1.1.1/go.mod 
h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
 github.com/google/uuid v1.2.0 h1:qJYtXnJRWmpe7m/3XlyhrsLrEURqHRM2kxzoxXqyUDs=
 github.com/google/uuid v1.2.0/go.mod 
h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
@@ -356,7 +349,6 @@ github.com/grpc-ecosystem/go-grpc-middleware v1.2.2/go.mod 
h1:EaizFBKfUKtMIF5iaD
 github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 
h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho=
 github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod 
h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
 github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod 
h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
-github.com/grpc-ecosystem/grpc-gateway v1.9.5 
h1:UImYN5qQ8tuGpGE16ZmjvcTtTw24zw1QAp/SlnNrZhI=
 github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod 
h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
 github.com/grpc-ecosystem/grpc-gateway v1.14.6/go.mod 
h1:zdiPV4Yse/1gnckTHtghG4GkDEdKCRJduHpTxT3/jcw=
 github.com/grpc-ecosystem/grpc-gateway v1.16.0 
h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo=
@@ -476,7 +468,6 @@ github.com/jinzhu/copier v0.0.0-20190625015134-976e0346caa8 
h1:mGIXW/lubQ4B+3bXT
 github.com/jinzhu/copier v0.0.0-20190625015134-976e0346caa8/go.mod 
h1:yL958EeXv8Ylng6IfnvG4oflryUi3vgA3xPs9hmII1s=
 github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af 
h1:pmfjZENx5imkbgOkpRUYLnmbU7UEFbjtDA2hxJ1ichM=
 github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod 
h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
-github.com/jonboulle/clockwork v0.1.0 
h1:VKV+ZcuP6l3yW9doeqz6ziZGgcynBVQO+obU0+0hcPo=
 github.com/jonboulle/clockwork v0.1.0/go.mod 
h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
 github.com/jonboulle/clockwork v0.2.2 
h1:UOGuzwb1PwsrDAObMuhUnj0p5ULPj8V/xJ7Kx9qUBdQ=
 github.com/jonboulle/clockwork v0.2.2/go.mod 
h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8=
@@ -510,7 +501,6 @@ github.com/konsorten/go-windows-terminal-sequences v1.0.3 
h1:CE8S1cTafDpPvMhIxNJ
 github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod 
h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
 github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod 
h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
 github.com/kr/pretty v0.1.0/go.mod 
h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
-github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs=
 github.com/kr/pretty v0.2.0/go.mod 
h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
 github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
 github.com/kr/text v0.1.0/go.mod 
h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
@@ -524,7 +514,6 @@ github.com/lightstep/lightstep-tracer-common/golang/gogo 
v0.0.0-20190605223551-b
 github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod 
h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4=
 github.com/linode/linodego v0.7.1 
h1:4WZmMpSA2NRwlPZcc0+4Gyn7rr99Evk9bnr0B3gXRKE=
 github.com/linode/linodego v0.7.1/go.mod 
h1:ga11n3ivecUrPCHN0rANxKmfWBJVkOXfLMZinAbj2sY=
-github.com/lyft/protoc-gen-validate v0.0.13/go.mod 
h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ=
 github.com/magiconair/properties v1.8.0/go.mod 
h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
 github.com/magiconair/properties v1.8.1/go.mod 
h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
 github.com/magiconair/properties v1.8.4 
h1:8KGKTcQQGm0Kv7vEbKFErAoAOFyyacLStRtQSeYtvkY=
@@ -718,7 +707,6 @@ github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 
h1:nn5Wsu0esKSJiIVhscUt
 github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod 
h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
 github.com/shirou/gopsutil v0.0.0-20181107111621-48177ef5f880/go.mod 
h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
 github.com/shirou/gopsutil 
v3.20.11-0.20201116082039-2fb5da2f2449+incompatible/go.mod 
h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
-github.com/shirou/gopsutil v3.20.11+incompatible 
h1:LJr4ZQK4mPpIV5gOa4jCOKOGb4ty4DZO54I4FGqIpto=
 github.com/shirou/gopsutil v3.20.11+incompatible/go.mod 
h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
 github.com/shirou/gopsutil v3.21.3+incompatible 
h1:uenXGGa8ESCQq+dbgtl916dmg6PSAz2cXov0uORQ9v8=
 github.com/shirou/gopsutil v3.21.3+incompatible/go.mod 
h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
@@ -773,7 +761,6 @@ github.com/stretchr/testify v1.2.2/go.mod 
h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf
 github.com/stretchr/testify v1.3.0/go.mod 
h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
 github.com/stretchr/testify v1.4.0/go.mod 
h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
 github.com/stretchr/testify v1.5.1/go.mod 
h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
-github.com/stretchr/testify v1.6.1 
h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
 github.com/stretchr/testify v1.6.1/go.mod 
h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
 github.com/stretchr/testify v1.7.0 
h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
 github.com/stretchr/testify v1.7.0/go.mod 
h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
@@ -820,6 +807,7 @@ go.etcd.io/bbolt v1.3.3/go.mod 
h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
 go.etcd.io/bbolt v1.3.4 h1:hi1bXHMVrlQh6WwxAy+qZCV/SYIlqo+Ushwdpa4tAKg=
 go.etcd.io/bbolt v1.3.4/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ=
 go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod 
h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg=
+go.etcd.io/etcd v0.0.0-20200402134248-51bdeb39e698 
h1:jWtjCJX1qxhHISBMLRztWwR+EXkI7MJAF2HjHAE/x/I=
 go.etcd.io/etcd v0.0.0-20200402134248-51bdeb39e698/go.mod 
h1:YoUyTScD3Vcv2RBm3eGVOq7i1ULiz3OuXoQFWOirmAM=
 go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
 go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
@@ -859,8 +847,9 @@ golang.org/x/crypto 
v0.0.0-20191002192127-34f69633bfdc/go.mod h1:yigFU9vqHzYiE8U
 golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod 
h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod 
h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
 golang.org/x/crypto v0.0.0-20200220183623-bac4c82f6975/go.mod 
h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
-golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 
h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI=
 golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod 
h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/crypto v0.0.0-20210415154028-4f45737414dc 
h1:+q90ECDSAQirdykUN6sPEiBXBsp8Csjcca8Oy7bgLTA=
+golang.org/x/crypto v0.0.0-20210415154028-4f45737414dc/go.mod 
h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
 golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod 
h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod 
h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod 
h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@@ -869,7 +858,6 @@ golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod 
h1:JXzH8nQsPlswgeRAPE
 golang.org/x/exp v0.0.0-20200331195152-e8c3332aa8e5/go.mod 
h1:4M0jN8W1tt0AVLNr8HDosyJCDCDuyL9N9+3m7wDWgKw=
 golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod 
h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
 golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod 
h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
-golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod 
h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
 golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod 
h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
 golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod 
h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
 golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod 
h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
@@ -914,13 +902,12 @@ golang.org/x/net 
v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLL
 golang.org/x/net v0.0.0-20200421231249-e086a090c8fd/go.mod 
h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
 golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod 
h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
 golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod 
h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
-golang.org/x/net v0.0.0-20200822124328-c89045814202 
h1:VvcQYSHwXgi7W+TpUR6A9g6Up98WAHf3f/ulnJ62IyA=
 golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod 
h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
-golang.org/x/net v0.0.0-20201021035429-f5854403a974 
h1:IX6qOQeG5uLjB/hjjwjedwfjND0hgjPMMyO1RoIXQNI=
 golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod 
h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
+golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 
h1:qWPm9rbaAMKs8Bq/9LRpbMqxWRVUAQwMI9fVrssnTfw=
+golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod 
h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
 golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod 
h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
 golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod 
h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
-golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 
h1:SVwTIAaPC2U/AvvLNZ2a7OVsmBpC8L5BlwK1whH3hm0=
 golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod 
h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
 golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d 
h1:TzXSXBo42m9gQenoE3b9BGiEpg5IG2JkU5FkPIawgtw=
 golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod 
h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -930,13 +917,11 @@ golang.org/x/sync 
v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJ
 golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod 
h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod 
h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod 
h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208 
h1:qwRHBd0NqMbJxfbotnDhm2ByMI1Shq4Y6oRJo21SGJA=
 golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod 
h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9 
h1:SQFwaSi55rU7vdNs9Yr0Z324VNlrF+0wMqRXT4St8ck=
 golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod 
h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea/go.mod 
h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod 
h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod 
h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod 
h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod 
h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod 
h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -979,32 +964,32 @@ golang.org/x/sys 
v0.0.0-20200420163511-1957bb5e6d1f/go.mod h1:h1NjWce9XRLGQEsW7w
 golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod 
h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod 
h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod 
h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod 
h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20201214210602-f9fddec55a1e/go.mod 
h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20201223074533-0d417f636930 
h1:vRgIt+nup/B/BwIS0g2oC0haq0iqbV3ZA+u6+0TlNCo=
 golang.org/x/sys v0.0.0-20201223074533-0d417f636930/go.mod 
h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210316164454-77fc1eacc6aa 
h1:ZYxPR6aca/uhfRJyaOAtflSHjJYiktO7QnJC5ut7iY4=
 golang.org/x/sys v0.0.0-20210316164454-77fc1eacc6aa/go.mod 
h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210415045647-66c3f260301c 
h1:6L+uOeS3OQt/f4eFHXZcTxeZrGCuz+CLElgEBjbcTA4=
+golang.org/x/sys v0.0.0-20210415045647-66c3f260301c/go.mod 
h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 
h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E=
+golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod 
h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod 
h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod 
h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.1-0.20181227161524-e6919f6577db/go.mod 
h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
-golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
 golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
-golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
 golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M=
+golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod 
h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod 
h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod 
h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
-golang.org/x/time v0.0.0-20191024005414-555d28b269f0 
h1:/5xXl8Y5W96D+TtHSlonuFqGHIWVuyCkGJLwGh9JJFs=
 golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod 
h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 golang.org/x/time v0.0.0-20201208040808-7e3f01d25324 
h1:Hir2P/De0WpUhtrKGGjvSb2YxUgyZ7EFOSLIcSSpiwE=
 golang.org/x/time v0.0.0-20201208040808-7e3f01d25324/go.mod 
h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod 
h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
-golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod 
h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod 
h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 golang.org/x/tools v0.0.0-20181011042414-1f849cf54d09/go.mod 
h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod 
h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
-golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod 
h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod 
h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
 golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod 
h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
 golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod 
h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
@@ -1028,7 +1013,6 @@ golang.org/x/tools 
v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapK
 golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod 
h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
 golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod 
h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
 golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod 
h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
-golang.org/x/tools v0.0.0-20200928182047-19e03678916f 
h1:VwGa2Wf+rHGIxvsssCkUNIyFv8jQY0VCBCNWtikoWq0=
 golang.org/x/tools v0.0.0-20200928182047-19e03678916f/go.mod 
h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU=
 golang.org/x/tools v0.0.0-20210106214847-113979e3529a 
h1:CB3a9Nez8M13wwlr/E2YtwoU+qYHKfC+JrDa45RXXoQ=
 golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod 
h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
@@ -1044,7 +1028,6 @@ google.golang.org/api v0.8.0/go.mod 
h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEn
 google.golang.org/api v0.9.0/go.mod 
h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
 google.golang.org/api v0.13.0 h1:Q3Ui3V3/CVinFWFiW39Iw0kMuVrRzYX0wN6OPFp0lTA=
 google.golang.org/api v0.13.0/go.mod 
h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
-google.golang.org/appengine v1.1.0/go.mod 
h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
 google.golang.org/appengine v1.2.0/go.mod 
h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
 google.golang.org/appengine v1.4.0/go.mod 
h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
 google.golang.org/appengine v1.5.0/go.mod 
h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
@@ -1065,30 +1048,17 @@ google.golang.org/genproto 
v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvx
 google.golang.org/genproto v0.0.0-20200423170343-7949de9c1215/go.mod 
h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
 google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884 
h1:fiNLklpBwWK1mth30Hlwk+fcdBmIALlgF5iy77O37Ig=
 google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod 
h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
-google.golang.org/grpc v1.14.0/go.mod 
h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
-google.golang.org/grpc v1.17.0/go.mod 
h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
-google.golang.org/grpc v1.19.0/go.mod 
h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
-google.golang.org/grpc v1.19.1/go.mod 
h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
-google.golang.org/grpc v1.20.0/go.mod 
h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM=
-google.golang.org/grpc v1.20.1/go.mod 
h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
-google.golang.org/grpc v1.21.0/go.mod 
h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
-google.golang.org/grpc v1.21.1/go.mod 
h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
-google.golang.org/grpc v1.22.0/go.mod 
h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
-google.golang.org/grpc v1.22.1/go.mod 
h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
-google.golang.org/grpc v1.23.0/go.mod 
h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
-google.golang.org/grpc v1.23.1/go.mod 
h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
-google.golang.org/grpc v1.25.1/go.mod 
h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
 google.golang.org/grpc v1.26.0 h1:2dTRdpdFEEhJYQD8EMLB61nnrzSCTbG38PhqdhvOltg=
 google.golang.org/grpc v1.26.0/go.mod 
h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
-google.golang.org/grpc v1.27.0/go.mod 
h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
-google.golang.org/grpc v1.29.1/go.mod 
h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
 google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod 
h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
 google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod 
h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
 google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod 
h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
 google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod 
h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
 google.golang.org/protobuf v1.21.0/go.mod 
h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
-google.golang.org/protobuf v1.23.0 
h1:4MY060fB1DLGMB/7MBTLnwQUY6+F09GEiz6SsrNqyzM=
 google.golang.org/protobuf v1.23.0/go.mod 
h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
+google.golang.org/protobuf v1.26.0-rc.1/go.mod 
h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
+google.golang.org/protobuf v1.26.0 
h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk=
+google.golang.org/protobuf v1.26.0/go.mod 
h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
 gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod 
h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U=
 gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod 
h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
 gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d/go.mod 
h1:cuepJuh7vyXfUyUwEgHQXw849cJrilpS5NeIjOWESAw=
@@ -1126,7 +1096,6 @@ gopkg.in/yaml.v2 v2.4.0 
h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
 gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
 gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c 
h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
 gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod 
h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
-honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod 
h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
 honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod 
h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
 honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod 
h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
 honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod 
h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
@@ -1151,7 +1120,6 @@ k8s.io/utils v0.0.0-20190801114015-581e00157fb1/go.mod 
h1:sZAwmy6armz5eXlNoLmJcl
 launchpad.net/gocheck v0.0.0-20140225173054-000000000087/go.mod 
h1:hj7XX3B/0A+80Vse0e+BUHsHMTEhd0O4cpUHr/e/BUM=
 rsc.io/binaryregexp v0.2.0/go.mod 
h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
 sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod 
h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI=
-sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs=
 sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
 sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q=
 sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=
diff --git a/pkg/config/api_config.go b/pkg/config/api_config.go
index 1a97921..d7b4e44 100644
--- a/pkg/config/api_config.go
+++ b/pkg/config/api_config.go
@@ -18,36 +18,49 @@
 package config
 
 import (
+       "regexp"
+       "strconv"
        "strings"
        "sync"
        "time"
 )
 
 import (
-       "github.com/coreos/etcd/mvcc/mvccpb"
-
        fc "github.com/dubbogo/dubbo-go-pixiu-filter/pkg/api/config"
+       etcdv3 "github.com/dubbogo/gost/database/kv/etcd/v3"
        perrors "github.com/pkg/errors"
+       "go.etcd.io/etcd/clientv3"
+       "go.etcd.io/etcd/mvcc/mvccpb"
 )
 
 import (
        "github.com/apache/dubbo-go-pixiu/pkg/common/yaml"
        "github.com/apache/dubbo-go-pixiu/pkg/logger"
        "github.com/apache/dubbo-go-pixiu/pkg/model"
-       etcdv3 "github.com/apache/dubbo-go-pixiu/pkg/remoting/etcd3"
 )
 
 var (
        apiConfig *fc.APIConfig
        once      sync.Once
        client    *etcdv3.Client
-       listener  APIConfigListener
+       listener  APIConfigResourceListener
        lock      sync.RWMutex
 )
 
-// APIConfigListener defines api config listener interface
-type APIConfigListener interface {
-       APIConfigChange(apiConfig fc.APIConfig) bool // bool is return for 
interface implement is interesting
+// APIConfigResourceListener defines api resource and method config listener 
interface
+type APIConfigResourceListener interface {
+       // ResourceChange handle modify resource event
+       ResourceChange(new fc.Resource, old fc.Resource) bool // bool is return 
for interface implement is interesting
+       // ResourceAdd handle add resource event
+       ResourceAdd(res fc.Resource) bool
+       // ResourceDelete handle delete resource event
+       ResourceDelete(deleted fc.Resource) bool
+       // MethodChange handle modify method event
+       MethodChange(res fc.Resource, method fc.Method, old fc.Method) bool
+       // MethodAdd handle add method below one resource event
+       MethodAdd(res fc.Resource, method fc.Method) bool
+       // MethodDelete handle delete method event
+       MethodDelete(res fc.Resource, method fc.Method) bool
 }
 
 // LoadAPIConfigFromFile load the api config from file
@@ -67,64 +80,144 @@ func LoadAPIConfigFromFile(path string) (*fc.APIConfig, 
error) {
 
 // LoadAPIConfig load the api config from config center
 func LoadAPIConfig(metaConfig *model.APIMetaConfig) (*fc.APIConfig, error) {
-       client = etcdv3.NewConfigClient(
+       tmpClient, err := etcdv3.NewConfigClientWithErr(
                etcdv3.WithName(etcdv3.RegistryETCDV3Client),
                etcdv3.WithTimeout(10*time.Second),
                etcdv3.WithEndpoints(strings.Split(metaConfig.Address, ",")...),
        )
+       if err != nil {
+               return nil, perrors.Errorf("Init etcd client fail error %v", 
err)
+       }
 
-       go listenAPIConfigNodeEvent(metaConfig.APIConfigPath)
-
-       content, err := client.Get(metaConfig.APIConfigPath)
+       client = tmpClient
+       kList, vList, err := client.GetChildren(metaConfig.APIConfigPath)
        if err != nil {
                return nil, perrors.Errorf("Get remote config fail error %v", 
err)
        }
-
-       if err = initAPIConfigFromString(content); err != nil {
+       if err = initAPIConfigFromKVList(kList, vList); err != nil {
                return nil, err
        }
-
+       // TODO: init other setting which need fetch from remote
+       go listenResourceAndMethodEvent(metaConfig.APIConfigPath)
+       // TODO: watch other setting which need fetch from remote
        return apiConfig, nil
 }
 
-func initAPIConfigFromString(content string) error {
+func initAPIConfigFromKVList(kList, vList []string) error {
+       var skList, svList, mkList, mvList []string
+
+       for i, k := range kList {
+               v := vList[i]
+               // handle resource
+               re := getCheckResourceRegexp()
+               if m := re.Match([]byte(k)); m {
+                       skList = append(skList, k)
+                       svList = append(svList, v)
+                       continue
+               }
+               // handle method
+               re = getExtractMethodRegexp()
+               if m := re.Match([]byte(k)); m {
+                       mkList = append(mkList, k)
+                       mvList = append(mvList, v)
+                       continue
+               }
+       }
+
        lock.Lock()
        defer lock.Unlock()
 
-       apiConf := &fc.APIConfig{}
-       if len(content) != 0 {
-               err := yaml.UnmarshalYML([]byte(content), apiConf)
+       tmpApiConf := &fc.APIConfig{}
+       if err := initAPIConfigServiceFromKvList(tmpApiConf, skList, svList); 
err != nil {
+               logger.Error("initAPIConfigServiceFromKvList error %v", 
err.Error())
+               return err
+       }
+       if err := initAPIConfigMethodFromKvList(tmpApiConf, mkList, mvList); 
err != nil {
+               logger.Error("initAPIConfigMethodFromKvList error %v", 
err.Error())
+               return err
+       }
+
+       apiConfig = tmpApiConf
+       return nil
+}
+
+func initAPIConfigMethodFromKvList(config *fc.APIConfig, kList, vList 
[]string) error {
+       for i, _ := range kList {
+               v := vList[i]
+               method := &fc.Method{}
+               err := yaml.UnmarshalYML([]byte(v), method)
                if err != nil {
-                       return perrors.Errorf("unmarshalYmlConfig error %v", 
perrors.WithStack(err))
+                       logger.Error("unmarshalYmlConfig error %v", err.Error())
+                       return err
                }
 
-               valid := validateAPIConfig(apiConf)
-               if !valid {
-                       return perrors.Errorf("api config not valid error %v", 
perrors.WithStack(err))
+               found := false
+               for r, resource := range config.Resources {
+                       if method.ResourcePath != resource.Path {
+                               continue
+                       }
+
+                       for j, old := range resource.Methods {
+                               if old.HTTPVerb == method.HTTPVerb {
+                                       // modify one method
+                                       resource.Methods[j] = *method
+                                       found = true
+                               }
+                       }
+                       if !found {
+                               resource.Methods = append(resource.Methods, 
*method)
+                               config.Resources[r] = resource
+                               found = true
+                       }
                }
 
-               apiConfig = apiConf
+               // not found one resource, so need add empty resource first
+               if !found {
+                       resource := &fc.Resource{}
+                       resource.Methods = append(resource.Methods, *method)
+                       resource.Path = method.ResourcePath
+                       config.Resources = append(config.Resources, *resource)
+               }
        }
        return nil
 }
 
-// validateAPIConfig check api config valid
-func validateAPIConfig(conf *fc.APIConfig) bool {
-       if conf.Name == "" {
-               return false
-       }
-       if conf.Description == "" {
-               return false
-       }
-       if conf.Resources == nil || len(conf.Resources) == 0 {
-               return false
+func initAPIConfigServiceFromKvList(config *fc.APIConfig, kList, vList 
[]string) error {
+       for i, _ := range kList {
+               v := vList[i]
+               resource := &fc.Resource{}
+               err := yaml.UnmarshalYML([]byte(v), resource)
+               if err != nil {
+                       logger.Error("unmarshalYmlConfig error %v", err.Error())
+                       return err
+               }
+
+               found := false
+               if config.Resources == nil {
+                       config.Resources = make([]fc.Resource, 0)
+               }
+
+               for i, old := range config.Resources {
+                       if old.Path != resource.Path {
+                               continue
+                       }
+                       // replace old with new one except method list
+                       resource.Methods = old.Methods
+                       config.Resources[i] = *resource
+                       found = true
+               }
+
+               if !found {
+                       config.Resources = append(config.Resources, *resource)
+               }
+               continue
        }
-       return true
+       return nil
 }
 
-func listenAPIConfigNodeEvent(key string) bool {
+func listenResourceAndMethodEvent(key string) bool {
        for {
-               wc, err := client.Watch(key)
+               wc, err := client.WatchWithOption(key, clientv3.WithPrefix())
                if err != nil {
                        logger.Warnf("Watch api config {key:%s} = error{%v}", 
key, err)
                        return false
@@ -151,11 +244,11 @@ func listenAPIConfigNodeEvent(key string) bool {
                        for _, event := range e.Events {
                                switch event.Type {
                                case mvccpb.PUT:
-                                       if err = 
initAPIConfigFromString(string(event.Kv.Value)); err == nil {
-                                               
listener.APIConfigChange(GetAPIConf())
-                                       }
+                                       logger.Infof("get event (key{%s}) = 
event{EventNodePut}", event.Kv.Key)
+                                       handlePutEvent(event.Kv.Key, 
event.Kv.Value)
                                case mvccpb.DELETE:
-                                       logger.Warnf("get event (key{%s}) = 
event{EventNodeDeleted}", event.Kv.Key)
+                                       logger.Infof("get event (key{%s}) = 
event{EventNodeDeleted}", event.Kv.Key)
+                                       handleDeleteEvent(event.Kv.Key, 
event.Kv.Value)
                                        return true
                                default:
                                        return false
@@ -165,12 +258,167 @@ func listenAPIConfigNodeEvent(key string) bool {
        }
 }
 
+func handleDeleteEvent(key, val []byte) {
+       lock.Lock()
+       defer lock.Unlock()
+
+       keyStr := string(key)
+       keyStr = strings.TrimSuffix(keyStr, "/")
+
+       re := getCheckResourceRegexp()
+       if m := re.Match(key); m {
+               pathArray := strings.Split(keyStr, "/")
+               if len(pathArray) == 0 {
+                       logger.Errorf("handleDeleteEvent key format error")
+                       return
+               }
+               resourceIdStr := pathArray[len(pathArray)-1]
+               ID, err := strconv.Atoi(resourceIdStr)
+               if err != nil {
+                       logger.Error("handleDeleteEvent ID is not int error 
%v", err)
+                       return
+               }
+               deleteApiConfigResource(ID)
+               return
+       }
+
+       re = getExtractMethodRegexp()
+       if m := re.Match(key); m {
+               pathArray := strings.Split(keyStr, "/")
+               if len(pathArray) < 3 {
+                       logger.Errorf("handleDeleteEvent key format error")
+                       return
+               }
+               resourceIdStr := pathArray[len(pathArray)-3]
+               resourceId, err := strconv.Atoi(resourceIdStr)
+               if err != nil {
+                       logger.Error("handleDeleteEvent ID is not int error 
%v", err)
+                       return
+               }
+
+               methodIdStr := pathArray[len(pathArray)-1]
+               methodId, err := strconv.Atoi(methodIdStr)
+               if err != nil {
+                       logger.Error("handleDeleteEvent ID is not int error 
%v", err)
+                       return
+               }
+               deleteApiConfigMethod(resourceId, methodId)
+       }
+}
+
+func handlePutEvent(key, val []byte) {
+       lock.Lock()
+       defer lock.Unlock()
+
+       re := getCheckResourceRegexp()
+       if m := re.Match(key); m {
+               res := &fc.Resource{}
+               err := yaml.UnmarshalYML(val, res)
+               if err != nil {
+                       logger.Error("handlePutEvent UnmarshalYML error %v", 
err)
+                       return
+               }
+               mergeApiConfigResource(*res)
+               return
+       }
+
+       re = getExtractMethodRegexp()
+       if m := re.Match(key); m {
+               res := &fc.Method{}
+               err := yaml.UnmarshalYML(val, res)
+               if err != nil {
+                       logger.Error("handlePutEvent UnmarshalYML error %v", 
err)
+                       return
+               }
+               mergeApiConfigMethod(res.ResourcePath, *res)
+       }
+}
+
+func deleteApiConfigResource(resourceId int) {
+       for i := 0; i < len(apiConfig.Resources); i++ {
+               itr := apiConfig.Resources[i]
+               if itr.ID == resourceId {
+                       apiConfig.Resources = append(apiConfig.Resources[:i], 
apiConfig.Resources[i+1:]...)
+                       listener.ResourceDelete(itr)
+                       return
+               }
+       }
+}
+
+func mergeApiConfigResource(val fc.Resource) {
+       for i, resource := range apiConfig.Resources {
+               if val.ID != resource.ID {
+                       continue
+               }
+               // modify one resource
+               val.Methods = resource.Methods
+               apiConfig.Resources[i] = val
+               listener.ResourceChange(val, resource)
+               return
+       }
+       // add one resource
+       apiConfig.Resources = append(apiConfig.Resources, val)
+       listener.ResourceAdd(val)
+}
+
+func deleteApiConfigMethod(resourceId, methodId int) {
+       for _, resource := range apiConfig.Resources {
+               if resource.ID != resourceId {
+                       continue
+               }
+
+               for i := 0; i < len(resource.Methods); i++ {
+                       method := resource.Methods[i]
+
+                       if method.ID == methodId {
+                               resource.Methods = append(resource.Methods[:i], 
resource.Methods[i+1:]...)
+                               listener.MethodDelete(resource, method)
+                               return
+                       }
+               }
+       }
+}
+
+func mergeApiConfigMethod(path string, val fc.Method) {
+       for i, resource := range apiConfig.Resources {
+               if path != resource.Path {
+                       continue
+               }
+
+               for j, method := range resource.Methods {
+                       if method.ID == val.ID {
+                               // modify one method
+                               resource.Methods[j] = val
+                               listener.MethodChange(resource, val, method)
+                               apiConfig.Resources[i] = resource
+                               return
+                       }
+               }
+               // add one method
+               resource.Methods = append(resource.Methods, val)
+               apiConfig.Resources[i] = resource
+               listener.MethodAdd(resource, val)
+       }
+}
+
+func getCheckBaseInfoRegexp() *regexp.Regexp {
+       return regexp.MustCompile(".+/base$")
+}
+
+func getCheckResourceRegexp() *regexp.Regexp {
+       return regexp.MustCompile(".+/Resources/[^/]+/?$")
+}
+
+func getExtractMethodRegexp() *regexp.Regexp {
+       return regexp.MustCompile("Resources/([^/]+)/Method/[^/]+/?$")
+}
+
 // RegisterConfigListener register APIConfigListener
-func RegisterConfigListener(li APIConfigListener) {
+func RegisterConfigListener(li APIConfigResourceListener) {
        listener = li
 }
 
-// GetAPIConf returns the initted api config
+// GetAPIConf returns the init api config
 func GetAPIConf() fc.APIConfig {
        return *apiConfig
 }
diff --git a/pkg/pixiu/pixiu_start.go b/pkg/pixiu/pixiu_start.go
index 254eca7..d5e53c4 100644
--- a/pkg/pixiu/pixiu_start.go
+++ b/pkg/pixiu/pixiu_start.go
@@ -77,10 +77,8 @@ func (p *PX) Start() {
 
 func (p *PX) beforeStart() {
        dubbo.SingletonDubboClient().Init()
-
-       api.InitAPIsFromConfig(config.GetAPIConf())
-
        initialize.Run(config.GetAPIConf())
+       api.InitAPIsFromConfig(config.GetAPIConf())
 }
 
 // NewPX create pixiu
diff --git a/pkg/registry/zookeeper.go b/pkg/registry/zookeeper.go
index f84f43e..ada2452 100644
--- a/pkg/registry/zookeeper.go
+++ b/pkg/registry/zookeeper.go
@@ -26,7 +26,7 @@ import (
 import (
        "github.com/apache/dubbo-go-pixiu/pkg/logger"
        "github.com/apache/dubbo-go/common"
-       zookeeper "github.com/dubbogo/gost/database/kv/zk"
+       gxzookeeper "github.com/dubbogo/gost/database/kv/zk"
 )
 
 const (
@@ -40,13 +40,13 @@ func init() {
 // ZookeeperRegistryLoad load dubbo apis from zookeeper registry
 type ZookeeperRegistryLoad struct {
        zkName  string
-       client  *zookeeper.ZookeeperClient
+       client  *gxzookeeper.ZookeeperClient
        Address string
        cluster string
 }
 
 func newZookeeperRegistryLoad(address, cluster string) (Loader, error) {
-       newClient, err := zookeeper.NewZookeeperClient("zkClient", 
strings.Split(address, ","), false, zookeeper.WithZkTimeOut(15*time.Second))
+       newClient, err := gxzookeeper.NewZookeeperClient("zkClient", 
strings.Split(address, ","), false, gxzookeeper.WithZkTimeOut(15*time.Second))
        if err != nil {
                logger.Warnf("newZookeeperClient error:%v", err)
                return nil, err
diff --git a/pkg/registry/zookeeper_test.go b/pkg/registry/zookeeper_test.go
index d03ff12..8086cfe 100644
--- a/pkg/registry/zookeeper_test.go
+++ b/pkg/registry/zookeeper_test.go
@@ -24,24 +24,25 @@ import (
        "strings"
        "testing"
        "time"
+)
 
+import (
        "github.com/apache/dubbo-go/common"
        "github.com/apache/dubbo-go/common/constant"
        "github.com/dubbogo/go-zookeeper/zk"
-
-       zookeeper "github.com/dubbogo/gost/database/kv/zk"
+       gxzookeeper "github.com/dubbogo/gost/database/kv/zk"
        "github.com/stretchr/testify/assert"
 )
 
 const providerUrlstr = 
"dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider?methods.GetUser.retries=1"
 
-func newMockZkRegistry(t *testing.T, providerURL *common.URL, opts 
...zookeeper.Option) (*zk.TestCluster, error) {
+func newMockZkRegistry(t *testing.T, providerURL *common.URL, opts 
...gxzookeeper.Option) (*zk.TestCluster, error) {
        var (
                err    error
                c      *zk.TestCluster
-               client *zookeeper.ZookeeperClient
+               client *gxzookeeper.ZookeeperClient
        )
-       c, client, _, err = zookeeper.NewMockZookeeperClient("test", 
15*time.Second, opts...)
+       c, client, _, err = gxzookeeper.NewMockZookeeperClient("test", 
15*time.Second, opts...)
        if err != nil {
                return nil, err
        }
diff --git a/pkg/remoting/etcd3/client.go b/pkg/remoting/etcd3/client.go
deleted file mode 100644
index b2f8676..0000000
--- a/pkg/remoting/etcd3/client.go
+++ /dev/null
@@ -1,458 +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 etcdv3
-
-import (
-       "context"
-       "sync"
-       "time"
-)
-
-import (
-       "github.com/coreos/etcd/clientv3"
-       "github.com/coreos/etcd/clientv3/concurrency"
-       perrors "github.com/pkg/errors"
-       "google.golang.org/grpc"
-)
-
-import (
-       "github.com/apache/dubbo-go-pixiu/pkg/logger"
-)
-
-const (
-       // ConnDelay connection delay
-       ConnDelay = 3
-       // MaxFailTimes max failure times
-       MaxFailTimes = 15
-       // RegistryETCDV3Client client name
-       RegistryETCDV3Client = "etcd registry"
-       // MetadataETCDV3Client client name
-       MetadataETCDV3Client = "etcd metadata"
-)
-
-var (
-       // ErrNilETCDV3Client raw client nil
-       ErrNilETCDV3Client = perrors.New("etcd raw client is nil") // full 
describe the ERR
-       // ErrKVPairNotFound not found key
-       ErrKVPairNotFound = perrors.New("k/v pair not found")
-)
-
-// Options client configuration
-type Options struct {
-       name      string
-       endpoints []string
-       client    *Client
-       timeout   time.Duration
-       heartbeat int // heartbeat second
-}
-
-// Option will define a function of handling Options
-type Option func(*Options)
-
-// WithEndpoints sets etcd client endpoints
-func WithEndpoints(endpoints ...string) Option {
-       return func(opt *Options) {
-               opt.endpoints = endpoints
-       }
-}
-
-// WithName sets etcd client name
-func WithName(name string) Option {
-       return func(opt *Options) {
-               opt.name = name
-       }
-}
-
-// WithTimeout sets etcd client timeout
-func WithTimeout(timeout time.Duration) Option {
-       return func(opt *Options) {
-               opt.timeout = timeout
-       }
-}
-
-// WithHeartbeat sets etcd client heartbeat
-func WithHeartbeat(heartbeat int) Option {
-       return func(opt *Options) {
-               opt.heartbeat = heartbeat
-       }
-}
-
-// NewConfigClient create new Client
-func NewConfigClient(opts ...Option) *Client {
-       options := &Options{
-               heartbeat: 1, // default heartbeat
-       }
-       for _, opt := range opts {
-               opt(options)
-       }
-
-       newClient, err := NewClient(options.name, options.endpoints, 
options.timeout, options.heartbeat)
-       if err != nil {
-               logger.Errorf("new etcd client (name{%s}, etcd addresses{%v}, 
timeout{%d}) = error{%v}",
-                       options.name, options.endpoints, options.timeout, err)
-       }
-       return newClient
-}
-
-// Client represents etcd client Configuration
-type Client struct {
-       lock sync.RWMutex
-
-       // these properties are only set once when they are started.
-       name      string
-       endpoints []string
-       timeout   time.Duration
-       heartbeat int
-
-       ctx       context.Context    // if etcd server connection lose, the 
ctx.Done will be sent msg
-       cancel    context.CancelFunc // cancel the ctx, all watcher will stopped
-       rawClient *clientv3.Client
-
-       exit chan struct{}
-       Wait sync.WaitGroup
-}
-
-// NewClient create a client instance with name, endpoints etc.
-func NewClient(name string, endpoints []string, timeout time.Duration, 
heartbeat int) (*Client, error) {
-       ctx, cancel := context.WithCancel(context.Background())
-       rawClient, err := clientv3.New(clientv3.Config{
-               Context:     ctx,
-               Endpoints:   endpoints,
-               DialTimeout: timeout,
-               DialOptions: []grpc.DialOption{grpc.WithBlock()},
-       })
-       if err != nil {
-               cancel()
-               return nil, perrors.WithMessage(err, "new raw client block 
connect to server")
-       }
-
-       c := &Client{
-               name:      name,
-               timeout:   timeout,
-               endpoints: endpoints,
-               heartbeat: heartbeat,
-
-               ctx:       ctx,
-               cancel:    cancel,
-               rawClient: rawClient,
-
-               exit: make(chan struct{}),
-       }
-
-       if err := c.maintenanceStatus(); err != nil {
-               return nil, perrors.WithMessage(err, "client maintenance 
status")
-       }
-       return c, nil
-}
-
-// NOTICE: need to get the lock before calling this method
-func (c *Client) clean() {
-       // close raw client
-       c.rawClient.Close()
-
-       // cancel ctx for raw client
-       c.cancel()
-
-       // clean raw client
-       c.rawClient = nil
-}
-
-func (c *Client) stop() bool {
-       select {
-       case <-c.exit:
-               return true
-       default:
-               close(c.exit)
-       }
-       return false
-}
-
-// Close close client
-func (c *Client) Close() {
-       if c == nil {
-               return
-       }
-
-       // stop the client
-       c.stop()
-
-       // wait client maintenance status stop
-       c.Wait.Wait()
-
-       c.lock.Lock()
-       defer c.lock.Unlock()
-       if c.rawClient != nil {
-               c.clean()
-       }
-       logger.Warnf("etcd client{name:%s, endpoints:%s} exit now.", c.name, 
c.endpoints)
-}
-
-func (c *Client) maintenanceStatus() error {
-       s, err := concurrency.NewSession(c.rawClient, 
concurrency.WithTTL(c.heartbeat))
-       if err != nil {
-               return perrors.WithMessage(err, "new session with server")
-       }
-
-       // must add wg before go maintenance status goroutine
-       c.Wait.Add(1)
-       go c.maintenanceStatusLoop(s)
-       return nil
-}
-
-func (c *Client) maintenanceStatusLoop(s *concurrency.Session) {
-       defer func() {
-               c.Wait.Done()
-               logger.Infof("etcd client {endpoints:%v, name:%s} maintenance 
goroutine game over.", c.endpoints, c.name)
-       }()
-
-       for {
-               select {
-               case <-c.Done():
-                       // Client be stopped, will clean the client hold 
resources
-                       return
-               case <-s.Done():
-                       logger.Warn("etcd server stopped")
-                       c.lock.Lock()
-                       // when etcd server stopped, cancel ctx, stop all 
watchers
-                       c.clean()
-                       // when connection lose, stop client, trigger reconnect 
to etcd
-                       c.stop()
-                       c.lock.Unlock()
-                       return
-               }
-       }
-}
-
-// if k not exist will put k/v in etcd, otherwise return nil
-func (c *Client) put(k string, v string, opts ...clientv3.OpOption) error {
-       c.lock.RLock()
-       defer c.lock.RUnlock()
-
-       if c.rawClient == nil {
-               return ErrNilETCDV3Client
-       }
-
-       _, err := c.rawClient.Txn(c.ctx).
-               If(clientv3.Compare(clientv3.Version(k), "<", 1)).
-               Then(clientv3.OpPut(k, v, opts...)).
-               Commit()
-       return err
-}
-
-// if k not exist will put k/v in etcd
-// if k is already exist in etcd, replace it
-func (c *Client) update(k string, v string, opts ...clientv3.OpOption) error {
-       c.lock.RLock()
-       defer c.lock.RUnlock()
-
-       if c.rawClient == nil {
-               return ErrNilETCDV3Client
-       }
-
-       _, err := c.rawClient.Txn(c.ctx).
-               If(clientv3.Compare(clientv3.Version(k), "!=", -1)).
-               Then(clientv3.OpPut(k, v, opts...)).
-               Commit()
-       return err
-}
-
-func (c *Client) delete(k string) error {
-       c.lock.RLock()
-       defer c.lock.RUnlock()
-
-       if c.rawClient == nil {
-               return ErrNilETCDV3Client
-       }
-
-       _, err := c.rawClient.Delete(c.ctx, k)
-       return err
-}
-
-func (c *Client) get(k string) (string, error) {
-       c.lock.RLock()
-       defer c.lock.RUnlock()
-
-       if c.rawClient == nil {
-               return "", ErrNilETCDV3Client
-       }
-
-       resp, err := c.rawClient.Get(c.ctx, k)
-       if err != nil {
-               return "", err
-       }
-
-       if len(resp.Kvs) == 0 {
-               return "", ErrKVPairNotFound
-       }
-
-       return string(resp.Kvs[0].Value), nil
-}
-
-// CleanKV delete all key and value
-func (c *Client) CleanKV() error {
-       c.lock.RLock()
-       defer c.lock.RUnlock()
-
-       if c.rawClient == nil {
-               return ErrNilETCDV3Client
-       }
-
-       _, err := c.rawClient.Delete(c.ctx, "", clientv3.WithPrefix())
-       return err
-}
-
-func (c *Client) getChildren(k string) ([]string, []string, error) {
-       c.lock.RLock()
-       defer c.lock.RUnlock()
-
-       if c.rawClient == nil {
-               return nil, nil, ErrNilETCDV3Client
-       }
-
-       resp, err := c.rawClient.Get(c.ctx, k, clientv3.WithPrefix())
-       if err != nil {
-               return nil, nil, err
-       }
-
-       if len(resp.Kvs) == 0 {
-               return nil, nil, ErrKVPairNotFound
-       }
-
-       kList := make([]string, 0, len(resp.Kvs))
-       vList := make([]string, 0, len(resp.Kvs))
-       for _, kv := range resp.Kvs {
-               kList = append(kList, string(kv.Key))
-               vList = append(vList, string(kv.Value))
-       }
-       return kList, vList, nil
-}
-
-func (c *Client) watchWithPrefix(prefix string) (clientv3.WatchChan, error) {
-       c.lock.RLock()
-       defer c.lock.RUnlock()
-
-       if c.rawClient == nil {
-               return nil, ErrNilETCDV3Client
-       }
-
-       return c.rawClient.Watch(c.ctx, prefix, clientv3.WithPrefix()), nil
-}
-
-func (c *Client) watch(k string) (clientv3.WatchChan, error) {
-       c.lock.RLock()
-       defer c.lock.RUnlock()
-
-       if c.rawClient == nil {
-               return nil, ErrNilETCDV3Client
-       }
-
-       return c.rawClient.Watch(c.ctx, k), nil
-}
-
-func (c *Client) keepAliveKV(k string, v string) error {
-       c.lock.RLock()
-       defer c.lock.RUnlock()
-
-       if c.rawClient == nil {
-               return ErrNilETCDV3Client
-       }
-
-       // make lease time longer, since 1 second is too short
-       lease, err := c.rawClient.Grant(c.ctx, int64(30*time.Second.Seconds()))
-       if err != nil {
-               return perrors.WithMessage(err, "grant lease")
-       }
-
-       keepAlive, err := c.rawClient.KeepAlive(c.ctx, lease.ID)
-       if err != nil || keepAlive == nil {
-               c.rawClient.Revoke(c.ctx, lease.ID)
-               if err != nil {
-                       return perrors.WithMessage(err, "keep alive lease")
-               }
-               return perrors.New("keep alive lease")
-       }
-
-       _, err = c.rawClient.Put(c.ctx, k, v, clientv3.WithLease(lease.ID))
-       return perrors.WithMessage(err, "put k/v with lease")
-}
-
-// Done return exit chan
-func (c *Client) Done() <-chan struct{} {
-       return c.exit
-}
-
-// Valid check client
-func (c *Client) Valid() bool {
-       select {
-       case <-c.exit:
-               return false
-       default:
-       }
-
-       c.lock.RLock()
-       defer c.lock.RUnlock()
-       return c.rawClient != nil
-}
-
-// Create key value ...
-func (c *Client) Create(k string, v string) error {
-       err := c.put(k, v)
-       return perrors.WithMessagef(err, "put k/v (key: %s value %s)", k, v)
-}
-
-// Update key value ...
-func (c *Client) Update(k, v string) error {
-       err := c.update(k, v)
-       return perrors.WithMessagef(err, "Update k/v (key: %s value %s)", k, v)
-}
-
-// Delete key
-func (c *Client) Delete(k string) error {
-       err := c.delete(k)
-       return perrors.WithMessagef(err, "delete k/v (key %s)", k)
-}
-
-// RegisterTemp registers a temporary node
-func (c *Client) RegisterTemp(k, v string) error {
-       err := c.keepAliveKV(k, v)
-       return perrors.WithMessagef(err, "keepalive kv (key %s)", k)
-}
-
-// GetChildrenKVList gets children kv list by @k
-func (c *Client) GetChildrenKVList(k string) ([]string, []string, error) {
-       kList, vList, err := c.getChildren(k)
-       return kList, vList, perrors.WithMessagef(err, "get key children (key 
%s)", k)
-}
-
-// Get gets value by @k
-func (c *Client) Get(k string) (string, error) {
-       v, err := c.get(k)
-       return v, perrors.WithMessagef(err, "get key value (key %s)", k)
-}
-
-// Watch watches on spec key
-func (c *Client) Watch(k string) (clientv3.WatchChan, error) {
-       wc, err := c.watch(k)
-       return wc, perrors.WithMessagef(err, "watch prefix (key %s)", k)
-}
-
-// WatchWithPrefix watches on spec prefix
-func (c *Client) WatchWithPrefix(prefix string) (clientv3.WatchChan, error) {
-       wc, err := c.watchWithPrefix(prefix)
-       return wc, perrors.WithMessagef(err, "watch prefix (key %s)", prefix)
-}
diff --git a/pkg/remoting/etcd3/client_test.go 
b/pkg/remoting/etcd3/client_test.go
deleted file mode 100644
index 2bf51bb..0000000
--- a/pkg/remoting/etcd3/client_test.go
+++ /dev/null
@@ -1,399 +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 etcdv3
-
-import (
-       "net/url"
-       "os"
-       "path"
-       "reflect"
-       "strings"
-       "sync"
-       "testing"
-       "time"
-)
-
-import (
-       "github.com/coreos/etcd/embed"
-       "github.com/coreos/etcd/mvcc/mvccpb"
-       perrors "github.com/pkg/errors"
-       "github.com/stretchr/testify/assert"
-       "github.com/stretchr/testify/suite"
-       "google.golang.org/grpc/connectivity"
-)
-
-const defaultEtcdV3WorkDir = "/tmp/default-dubbo-go-remote.etcd"
-
-// tests dataset
-var tests = []struct {
-       input struct {
-               k string
-               v string
-       }
-}{
-       {input: struct {
-               k string
-               v string
-       }{k: "name", v: "scott.wang"}},
-       {input: struct {
-               k string
-               v string
-       }{k: "namePrefix", v: "prefix.scott.wang"}},
-       {input: struct {
-               k string
-               v string
-       }{k: "namePrefix1", v: "prefix1.scott.wang"}},
-       {input: struct {
-               k string
-               v string
-       }{k: "age", v: "27"}},
-}
-
-// test dataset prefix
-const prefix = "name"
-
-type ClientTestSuite struct {
-       suite.Suite
-
-       etcdConfig struct {
-               name      string
-               endpoints []string
-               timeout   time.Duration
-               heartbeat int
-       }
-
-       etcd *embed.Etcd
-
-       client *Client
-}
-
-// start etcd server
-func (suite *ClientTestSuite) SetupSuite() {
-       t := suite.T()
-
-       DefaultListenPeerURLs := "http://localhost:2382";
-       DefaultListenClientURLs := "http://localhost:2381";
-       lpurl, _ := url.Parse(DefaultListenPeerURLs)
-       lcurl, _ := url.Parse(DefaultListenClientURLs)
-       cfg := embed.NewConfig()
-       cfg.LPUrls = []url.URL{*lpurl}
-       cfg.LCUrls = []url.URL{*lcurl}
-       cfg.Dir = defaultEtcdV3WorkDir
-       e, err := embed.StartEtcd(cfg)
-       if err != nil {
-               t.Fatal(err)
-       }
-       select {
-       case <-e.Server.ReadyNotify():
-               t.Log("Server is ready!")
-       case <-time.After(60 * time.Second):
-               e.Server.Stop() // trigger a shutdown
-               t.Logf("Server took too long to start!")
-       }
-
-       suite.etcd = e
-       return
-}
-
-// stop etcd server
-func (suite *ClientTestSuite) TearDownSuite() {
-       suite.etcd.Close()
-       if err := os.RemoveAll(defaultEtcdV3WorkDir); err != nil {
-               suite.FailNow(err.Error())
-       }
-}
-
-func (suite *ClientTestSuite) setUpClient() *Client {
-       c, err := NewClient(suite.etcdConfig.name,
-               suite.etcdConfig.endpoints,
-               suite.etcdConfig.timeout,
-               suite.etcdConfig.heartbeat)
-       if err != nil {
-               suite.T().Fatal(err)
-       }
-       return c
-}
-
-// set up a client for suite
-func (suite *ClientTestSuite) SetupTest() {
-       c := suite.setUpClient()
-       c.CleanKV()
-       suite.client = c
-       return
-}
-
-func (suite *ClientTestSuite) TestClientClose() {
-       c := suite.client
-       t := suite.T()
-
-       defer c.Close()
-       if c.rawClient.ActiveConnection().GetState() != connectivity.Ready {
-               t.Fatal(suite.client.rawClient.ActiveConnection().GetState())
-       }
-}
-
-func (suite *ClientTestSuite) TestClientValid() {
-       c := suite.client
-       t := suite.T()
-
-       if !c.Valid() {
-               t.Fatal("client is not valid")
-       }
-       c.Close()
-       if suite.client.Valid() != false {
-               t.Fatal("client is valid")
-       }
-}
-
-func (suite *ClientTestSuite) TestClientDone() {
-       c := suite.client
-
-       go func() {
-               time.Sleep(2 * time.Second)
-               c.Close()
-       }()
-
-       c.Wait.Wait()
-
-       if c.Valid() {
-               suite.T().Fatal("client should be invalid then")
-       }
-}
-
-func (suite *ClientTestSuite) TestClientCreateKV() {
-       tests := tests
-
-       c := suite.client
-       t := suite.T()
-
-       defer suite.client.Close()
-
-       for _, tc := range tests {
-
-               k := tc.input.k
-               v := tc.input.v
-               expect := tc.input.v
-
-               if err := c.Create(k, v); err != nil {
-                       t.Fatal(err)
-               }
-
-               value, err := c.Get(k)
-               if err != nil {
-                       t.Fatal(err)
-               }
-
-               if value != expect {
-                       t.Fatalf("expect %v but get %v", expect, value)
-               }
-
-       }
-}
-
-func (suite *ClientTestSuite) TestClientDeleteKV() {
-       tests := tests
-       c := suite.client
-       t := suite.T()
-
-       defer c.Close()
-
-       for _, tc := range tests {
-
-               k := tc.input.k
-               v := tc.input.v
-               expect := ErrKVPairNotFound
-
-               if err := c.Create(k, v); err != nil {
-                       t.Fatal(err)
-               }
-
-               if err := c.Delete(k); err != nil {
-                       t.Fatal(err)
-               }
-
-               _, err := c.Get(k)
-               if perrors.Cause(err) == expect {
-                       continue
-               }
-
-               if err != nil {
-                       t.Fatal(err)
-               }
-       }
-}
-
-func (suite *ClientTestSuite) TestClientGetChildrenKVList() {
-       tests := tests
-
-       c := suite.client
-       t := suite.T()
-
-       var expectKList []string
-       var expectVList []string
-
-       for _, tc := range tests {
-
-               k := tc.input.k
-               v := tc.input.v
-
-               if strings.Contains(k, prefix) {
-                       expectKList = append(expectKList, k)
-                       expectVList = append(expectVList, v)
-               }
-
-               if err := c.Create(k, v); err != nil {
-                       t.Fatal(err)
-               }
-       }
-
-       kList, vList, err := c.GetChildrenKVList(prefix)
-       if err != nil {
-               t.Fatal(err)
-       }
-
-       if reflect.DeepEqual(expectKList, kList) && 
reflect.DeepEqual(expectVList, vList) {
-               return
-       }
-
-       t.Fatalf("expect keylist %v but got %v expect valueList %v but got %v 
", expectKList, kList, expectVList, vList)
-}
-
-func (suite *ClientTestSuite) TestClientWatch() {
-       tests := tests
-
-       c := suite.client
-       t := suite.T()
-
-       wg := sync.WaitGroup{}
-       wg.Add(1)
-
-       go func() {
-               defer wg.Done()
-
-               wc, err := c.watch(prefix)
-               if err != nil {
-                       assert.Error(t, err)
-                       return
-               }
-
-               events := make([]mvccpb.Event, 0)
-               var eCreate, eDelete mvccpb.Event
-
-               for e := range wc {
-                       for _, event := range e.Events {
-                               events = append(events, (mvccpb.Event)(*event))
-                               if event.Type == mvccpb.PUT {
-                                       eCreate = (mvccpb.Event)(*event)
-                               }
-                               if event.Type == mvccpb.DELETE {
-                                       eDelete = (mvccpb.Event)(*event)
-                               }
-                               t.Logf("type IsCreate %v k %s v %s", 
event.IsCreate(), event.Kv.Key, event.Kv.Value)
-                       }
-               }
-
-               assert.Equal(t, 2, len(events))
-               assert.Contains(t, events, eCreate)
-               assert.Contains(t, events, eDelete)
-       }()
-
-       for _, tc := range tests {
-
-               k := tc.input.k
-               v := tc.input.v
-
-               if err := c.Create(k, v); err != nil {
-                       t.Fatal(err)
-               }
-
-               if err := c.delete(k); err != nil {
-                       t.Fatal(err)
-               }
-       }
-
-       c.Close()
-
-       wg.Wait()
-}
-
-func (suite *ClientTestSuite) TestClientRegisterTemp() {
-       c := suite.client
-       observeC := suite.setUpClient()
-       t := suite.T()
-
-       wg := sync.WaitGroup{}
-       wg.Add(1)
-
-       go func() {
-               defer wg.Done()
-
-               completePath := path.Join("scott", "wang")
-               wc, err := observeC.watch(completePath)
-               if err != nil {
-                       assert.Error(t, err)
-                       return
-               }
-
-               events := make([]mvccpb.Event, 0)
-               var eCreate, eDelete mvccpb.Event
-
-               for e := range wc {
-                       for _, event := range e.Events {
-                               events = append(events, (mvccpb.Event)(*event))
-                               if event.Type == mvccpb.DELETE {
-                                       eDelete = (mvccpb.Event)(*event)
-                                       t.Logf("complete key (%s) is delete", 
completePath)
-                                       observeC.Close()
-                                       break
-                               }
-                               eCreate = (mvccpb.Event)(*event)
-                               t.Logf("type IsCreate %v k %s v %s", 
event.IsCreate(), event.Kv.Key, event.Kv.Value)
-                       }
-               }
-
-               assert.Equal(t, 2, len(events))
-               assert.Contains(t, events, eCreate)
-               assert.Contains(t, events, eDelete)
-       }()
-
-       err := c.RegisterTemp("scott/wang", "test")
-       if err != nil {
-               t.Fatal(err)
-       }
-
-       time.Sleep(2 * time.Second)
-       c.Close()
-
-       wg.Wait()
-}
-
-func TestClientSuite(t *testing.T) {
-       suite.Run(t, &ClientTestSuite{
-               etcdConfig: struct {
-                       name      string
-                       endpoints []string
-                       timeout   time.Duration
-                       heartbeat int
-               }{
-                       name:      "test",
-                       endpoints: []string{"localhost:2381"},
-                       timeout:   time.Second,
-                       heartbeat: 1,
-               },
-       })
-}
diff --git a/pkg/router/route.go b/pkg/router/route.go
index 1834696..9468833 100644
--- a/pkg/router/route.go
+++ b/pkg/router/route.go
@@ -120,6 +120,29 @@ func (rt *Route) FindAPI(fullPath string, httpverb 
config.HTTPVerb) (*router.API
        return nil, false
 }
 
+// DeleteNode delete node by fullPath
+func (rt *Route) DeleteNode(fullPath string) bool {
+       lowerPath := strings.ToLower(fullPath)
+       if _, found := rt.searchWildcard(lowerPath); found {
+               rt.lock.RLock()
+               defer rt.lock.RUnlock()
+               rt.tree.Remove(lowerPath)
+               return true
+       }
+       return false
+}
+
+// DeleteAPI delete api by fullPath and http verb
+func (rt *Route) DeleteAPI(fullPath string, httpverb config.HTTPVerb) bool {
+       if n, found := rt.findNode(fullPath); found {
+               rt.lock.RLock()
+               defer rt.lock.RUnlock()
+               delete(n.methods, httpverb)
+               return true
+       }
+       return false
+}
+
 func (rt *Route) findNode(fullPath string) (*Node, bool) {
        lowerPath := strings.ToLower(fullPath)
        var n interface{}
diff --git a/pkg/service/api/discovery_service.go 
b/pkg/service/api/discovery_service.go
index 52b6e3d..42e3720 100644
--- a/pkg/service/api/discovery_service.go
+++ b/pkg/service/api/discovery_service.go
@@ -27,7 +27,6 @@ import (
        "github.com/apache/dubbo-go-pixiu/pkg/common/constant"
        "github.com/apache/dubbo-go-pixiu/pkg/common/extension"
        pc "github.com/apache/dubbo-go-pixiu/pkg/config"
-       "github.com/apache/dubbo-go-pixiu/pkg/filter/plugins"
        "github.com/apache/dubbo-go-pixiu/pkg/router"
        "github.com/apache/dubbo-go-pixiu/pkg/service"
 )
@@ -74,13 +73,78 @@ func (ads *LocalMemoryAPIDiscoveryService) ClearAPI() error 
{
        return nil
 }
 
-// APIConfigChange to response to api config change
-func (ads *LocalMemoryAPIDiscoveryService) APIConfigChange(apiConfig 
config.APIConfig) bool {
-       ads.ClearAPI()
-       loadAPIFromResource("", apiConfig.Resources, nil, ads)
+// RemoveAPIByPath remove all api belonged to path
+func (ads *LocalMemoryAPIDiscoveryService) RemoveAPIByPath(deleted 
config.Resource) error {
+       _, groupPath := getDefaultPath()
+       fullPath := getFullPath(groupPath, deleted.Path)
 
-       plugins.Init(apiConfig.PluginsGroup, apiConfig.PluginFilePath, 
apiConfig.Resources)
-       return true
+       ads.router.DeleteNode(fullPath)
+       return nil
+}
+
+// RemoveAPIByPath remove all api
+func (ads *LocalMemoryAPIDiscoveryService) RemoveAPI(fullPath string, method 
config.Method) error {
+       ads.router.DeleteAPI(fullPath, method.HTTPVerb)
+       return nil
+}
+
+// ResourceChange handle modify resource event
+func (ads *LocalMemoryAPIDiscoveryService) ResourceChange(new config.Resource, 
old config.Resource) bool {
+       if err := modifyAPIFromResource(new, old, ads); err == nil {
+               return true
+       }
+       return false
+}
+
+// ResourceAdd handle add resource event
+func (ads *LocalMemoryAPIDiscoveryService) ResourceAdd(res config.Resource) 
bool {
+       parentPath, groupPath := getDefaultPath()
+
+       fullHeaders := make(map[string]string, 9)
+       if err := addAPIFromResource(res, ads, groupPath, parentPath, 
fullHeaders); err == nil {
+               return true
+       }
+       return false
+}
+
+// ResourceDelete handle delete resource event
+func (ads *LocalMemoryAPIDiscoveryService) ResourceDelete(deleted 
config.Resource) bool {
+       if err := deleteAPIFromResource(deleted, ads); err == nil {
+               return true
+       }
+       return false
+}
+
+// MethodChange handle modify method event
+func (ads *LocalMemoryAPIDiscoveryService) MethodChange(res config.Resource, 
new config.Method, old config.Method) bool {
+       _, groupPath := getDefaultPath()
+       fullPath := getFullPath(groupPath, res.Path)
+       fullHeaders := make(map[string]string, 9)
+       if err := modifyAPIFromMethod(fullPath, new, old, fullHeaders, ads); 
err == nil {
+               return true
+       }
+       return false
+}
+
+// MethodAdd handle add method event
+func (ads *LocalMemoryAPIDiscoveryService) MethodAdd(res config.Resource, 
method config.Method) bool {
+       _, groupPath := getDefaultPath()
+       fullPath := getFullPath(groupPath, res.Path)
+       fullHeaders := make(map[string]string, 9)
+       if err := addAPIFromMethod(fullPath, method, fullHeaders, ads); err == 
nil {
+               return true
+       }
+       return false
+}
+
+// MethodDelete handle delete method event
+func (ads *LocalMemoryAPIDiscoveryService) MethodDelete(res config.Resource, 
method config.Method) bool {
+       _, groupPath := getDefaultPath()
+       fullPath := getFullPath(groupPath, res.Path)
+       if err := deleteAPIFromMethod(fullPath, method, ads); err == nil {
+               return true
+       }
+       return false
 }
 
 // InitAPIsFromConfig inits the router from API config and to local cache
@@ -94,26 +158,13 @@ func InitAPIsFromConfig(apiConfig config.APIConfig) error {
        return loadAPIFromResource("", apiConfig.Resources, nil, 
localAPIDiscSrv)
 }
 
-// RefreshAPIsFromConfig fresh the router from API config and to local cache
-func RefreshAPIsFromConfig(apiConfig config.APIConfig) error {
-       localAPIDiscSrv := NewLocalMemoryAPIDiscoveryService()
-       if len(apiConfig.Resources) == 0 {
-               return nil
-       }
-       error := loadAPIFromResource("", apiConfig.Resources, nil, 
localAPIDiscSrv)
-       if error == nil {
-               
extension.SetAPIDiscoveryService(constant.LocalMemoryApiDiscoveryService, 
localAPIDiscSrv)
-       }
-       return error
-}
-
-func loadAPIFromResource(parrentPath string, resources []config.Resource, 
parentHeaders map[string]string, localSrv service.APIDiscoveryService) error {
+func loadAPIFromResource(parentPath string, resources []config.Resource, 
parentHeaders map[string]string, localSrv service.APIDiscoveryService) error {
        errStack := []string{}
        if len(resources) == 0 {
                return nil
        }
-       groupPath := parrentPath
-       if parrentPath == constant.PathSlash {
+       groupPath := parentPath
+       if parentPath == constant.PathSlash {
                groupPath = ""
        }
        fullHeaders := parentHeaders
@@ -121,21 +172,8 @@ func loadAPIFromResource(parrentPath string, resources 
[]config.Resource, parent
                fullHeaders = make(map[string]string, 9)
        }
        for _, resource := range resources {
-               fullPath := groupPath + resource.Path
-               if !strings.HasPrefix(resource.Path, constant.PathSlash) {
-                       errStack = append(errStack, fmt.Sprintf("Path %s in %s 
doesn't start with /", resource.Path, parrentPath))
-                       continue
-               }
-               for headerName, headerValue := range resource.Headers {
-                       fullHeaders[headerName] = headerValue
-               }
-               if len(resource.Resources) > 0 {
-                       if err := loadAPIFromResource(resource.Path, 
resource.Resources, fullHeaders, localSrv); err != nil {
-                               errStack = append(errStack, err.Error())
-                       }
-               }
-
-               if err := loadAPIFromMethods(fullPath, resource.Methods, 
fullHeaders, localSrv); err != nil {
+               err := addAPIFromResource(resource, localSrv, groupPath, 
parentPath, fullHeaders)
+               if err != nil {
                        errStack = append(errStack, err.Error())
                }
        }
@@ -145,16 +183,85 @@ func loadAPIFromResource(parrentPath string, resources 
[]config.Resource, parent
        return nil
 }
 
+func getDefaultPath() (string, string) {
+       return "", ""
+}
+
+func modifyAPIFromResource(new config.Resource, old config.Resource, localSrv 
service.APIDiscoveryService) error {
+       parentPath, groupPath := getDefaultPath()
+       fullHeaders := make(map[string]string, 9)
+
+       err := deleteAPIFromResource(old, localSrv)
+       if err != nil {
+               return err
+       }
+
+       err = addAPIFromResource(new, localSrv, groupPath, parentPath, 
fullHeaders)
+       return err
+}
+
+func deleteAPIFromResource(old config.Resource, localSrv 
service.APIDiscoveryService) error {
+       return localSrv.RemoveAPIByPath(old)
+}
+
+func addAPIFromResource(resource config.Resource, localSrv 
service.APIDiscoveryService, groupPath string, parentPath string, fullHeaders 
map[string]string) error {
+       fullPath := getFullPath(groupPath, resource.Path)
+       if !strings.HasPrefix(resource.Path, constant.PathSlash) {
+               return errors.New(fmt.Sprintf("Path %s in %s doesn't start with 
/", resource.Path, parentPath))
+       }
+       for headerName, headerValue := range resource.Headers {
+               fullHeaders[headerName] = headerValue
+       }
+       if len(resource.Resources) > 0 {
+               if err := loadAPIFromResource(resource.Path, 
resource.Resources, fullHeaders, localSrv); err != nil {
+                       return err
+               }
+       }
+
+       if err := loadAPIFromMethods(fullPath, resource.Methods, fullHeaders, 
localSrv); err != nil {
+               return err
+       }
+       return nil
+}
+
+func addAPIFromMethod(fullPath string, method config.Method, headers 
map[string]string, localSrv service.APIDiscoveryService) error {
+       api := fr.API{
+               URLPattern: fullPath,
+               Method:     method,
+               Headers:    headers,
+       }
+       if err := localSrv.AddAPI(api); err != nil {
+               return errors.New(fmt.Sprintf("Path: %s, Method: %s, error: 
%s", fullPath, method.HTTPVerb, err.Error()))
+       }
+       return nil
+}
+
+func modifyAPIFromMethod(fullPath string, new config.Method, old 
config.Method, headers map[string]string, localSrv service.APIDiscoveryService) 
error {
+       if err := localSrv.RemoveAPI(fullPath, old); err != nil {
+               return err
+       }
+
+       if err := addAPIFromMethod(fullPath, new, headers, localSrv); err != 
nil {
+               return err
+       }
+
+       return nil
+}
+
+func deleteAPIFromMethod(fullPath string, deleted config.Method, localSrv 
service.APIDiscoveryService) error {
+       return localSrv.RemoveAPI(fullPath, deleted)
+}
+
+func getFullPath(groupPath string, resourcePath string) string {
+       return groupPath + resourcePath
+}
+
 func loadAPIFromMethods(fullPath string, methods []config.Method, headers 
map[string]string, localSrv service.APIDiscoveryService) error {
        errStack := []string{}
        for _, method := range methods {
-               api := fr.API{
-                       URLPattern: fullPath,
-                       Method:     method,
-                       Headers:    headers,
-               }
-               if err := localSrv.AddAPI(api); err != nil {
-                       errStack = append(errStack, fmt.Sprintf("Path: %s, 
Method: %s, error: %s", fullPath, method.HTTPVerb, err.Error()))
+
+               if err := addAPIFromMethod(fullPath, method, headers, 
localSrv); err != nil {
+                       errStack = append(errStack, err.Error())
                }
        }
        if len(errStack) > 0 {
diff --git a/pkg/service/discovery_service.go b/pkg/service/discovery_service.go
index f4353fa..f26b1f6 100644
--- a/pkg/service/discovery_service.go
+++ b/pkg/service/discovery_service.go
@@ -63,10 +63,12 @@ var EmptyDiscoveryResponse = &DiscoveryResponse{}
 
 // APIDiscoveryService api discovery service interface
 type APIDiscoveryService interface {
-       pc.APIConfigListener
+       pc.APIConfigResourceListener
        AddAPI(router.API) error
        ClearAPI() error
        GetAPI(string, config.HTTPVerb) (router.API, error)
+       RemoveAPIByPath(deleted config.Resource) error
+       RemoveAPI(fullPath string, method config.Method) error
 }
 
 // DiscoveryService is come from envoy, it can used for admin
diff --git a/samples/admin/sample.md b/samples/admin/sample.md
index dd31c93..efa39a0 100644
--- a/samples/admin/sample.md
+++ b/samples/admin/sample.md
@@ -48,14 +48,11 @@ curl "http://127.0.0.1:8888/api/v1/test-dubbo/user?name=tc"; 
, the result:
 
 ## Start admin
 
-run cmd/admin/admin.go
+clone from https://github.com/dubbogo/pixiu-admin
 
-config program arguments:
-- -c /XXX/dubbo-go-proxy/samples/admin/admin/admin_config.yaml 
+cd pixiu-admin
 
+run cmd/admin/admin.go
 
-- run cmd `curl 127.0.0.1:8080/config/api` to check current proxy api config
-- modify api_config.yaml content, such as change path `test-dubbo/user` to 
`test-dubbo/user_new`
-- run cmd `curl "127.0.0.1:8080/config/api/set" -X POST --data-binary 
"@/xx/xx/dubbo-go-proxy/samples/admin/proxy/api_config.yaml"` to modify proxy 
api config
-- then, `curl "http://127.0.0.1:8888/api/v1/test-dubbo/user?name=tc"` , the 
result will be 404
-- run `curl "http://127.0.0.1:8888/api/v1/test-dubbo/user_new?name=tc"` get 
the correct result
+config program arguments:
+- -c /xx/pixiu-admin/configs/admin_config.yaml
\ No newline at end of file

Reply via email to