You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@dubbo.apache.org by al...@apache.org on 2021/10/22 11:22:16 UTC

[dubbo-go-samples] branch master updated: add tpslimit filter sample (#258)

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

alexstocks pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/dubbo-go-samples.git


The following commit(s) were added to refs/heads/master by this push:
     new b015031  add tpslimit filter sample (#258)
b015031 is described below

commit b015031b39902df678451a13d812ef5399cea739
Author: luky116 <38...@users.noreply.github.com>
AuthorDate: Fri Oct 22 19:22:11 2021 +0800

    add tpslimit filter sample (#258)
    
    * add tpslimit filter sample
    
    * fix bug
    
    * fix bug
    
    * reformat code
    
    * add comments for code
    
    * reformat code
    
    * readme文件修改
    
    * use fixedWindow strategy
    
    * use protocolIDs
---
 .run/filter/filter-tpslimit-client.run.xml         |  14 +++
 .run/filter/filter-tpslimit-server.run.xml         |  14 +++
 filter/tpslimit/README.md                          |  53 ++++++++++
 filter/tpslimit/README_zh.md                       |  53 ++++++++++
 filter/tpslimit/go-client/cmd/client.go            |  64 ++++++++++++
 .../tpslimit}/go-client/conf/dubbogo.yml           |  11 ++-
 filter/tpslimit/go-client/pkg/user.go              |  38 ++++++++
 filter/tpslimit/go-server/cmd/server.go            |  72 ++++++++++++++
 filter/tpslimit/go-server/conf/dubbogo.yml         |  29 ++++++
 filter/tpslimit/go-server/pkg/limit_strategy.go    |  78 +++++++++++++++
 filter/tpslimit/go-server/pkg/reject_handler.go    | 107 +++++++++++++++++++++
 filter/tpslimit/go-server/pkg/user.go              |  48 +++++++++
 .../go-server/tests/integration/main_test.go       |  61 ++++++++++++
 .../tests/integration/userprovider_test.go         |  50 ++++++++++
 tracing/jsonrpc/go-client/conf/dubbogo.yml         |   2 +-
 15 files changed, 688 insertions(+), 6 deletions(-)

diff --git a/.run/filter/filter-tpslimit-client.run.xml b/.run/filter/filter-tpslimit-client.run.xml
new file mode 100644
index 0000000..f3717f7
--- /dev/null
+++ b/.run/filter/filter-tpslimit-client.run.xml
@@ -0,0 +1,14 @@
+<component name="ProjectRunConfigurationManager">
+  <configuration default="false" name="filter-tpslimit-client" type="GoApplicationRunConfiguration" factoryName="Go Application">
+    <module name="dubbo-go-samples2" />
+    <working_directory value="$PROJECT_DIR$" />
+    <envs>
+      <env name="DUBBO_GO_CONFIG_PATH" value="$PROJECT_DIR$/filter/tpslimit/go-client/conf/dubbogo.yml" />
+    </envs>
+    <kind value="PACKAGE" />
+    <package value="github.com/apache/dubbo-go-samples/filter/tpslimit/go-client/cmd" />
+    <directory value="$PROJECT_DIR$" />
+    <filePath value="$PROJECT_DIR$/filter/tpslimit/go-client/cmd/client.go" />
+    <method v="2" />
+  </configuration>
+</component>
\ No newline at end of file
diff --git a/.run/filter/filter-tpslimit-server.run.xml b/.run/filter/filter-tpslimit-server.run.xml
new file mode 100644
index 0000000..a8bae88
--- /dev/null
+++ b/.run/filter/filter-tpslimit-server.run.xml
@@ -0,0 +1,14 @@
+<component name="ProjectRunConfigurationManager">
+  <configuration default="false" name="filter-tpslimit-server" type="GoApplicationRunConfiguration" factoryName="Go Application" singleton="false">
+    <module name="dubbo-go-samples2" />
+    <working_directory value="$PROJECT_DIR$" />
+    <envs>
+      <env name="DUBBO_GO_CONFIG_PATH" value="$PROJECT_DIR$/filter/tpslimit/go-server/conf/dubbogo.yml" />
+    </envs>
+    <kind value="PACKAGE" />
+    <package value="github.com/apache/dubbo-go-samples/filter/tpslimit/go-server/cmd" />
+    <directory value="$PROJECT_DIR$" />
+    <filePath value="$PROJECT_DIR$/filter/tpslimit/go-server/cmd/server.go" />
+    <method v="2" />
+  </configuration>
+</component>
\ No newline at end of file
diff --git a/filter/tpslimit/README.md b/filter/tpslimit/README.md
new file mode 100644
index 0000000..bd51a6b
--- /dev/null
+++ b/filter/tpslimit/README.md
@@ -0,0 +1,53 @@
+# TPSLimit Filter Sample
+
+### Background
+
+Dubbo-go has a built in filter for limiting TPS purpose - "tpslimit". It can be enabled by configuring on the provider side, furthermore, user can customize the TPS limit strategy and the return value after the request is rejected.
+
+### Example
+
+##### 1. Code
+
+A) Customize TPS limit strategy:
+
+To customize TPS limit strategy, the interface "filter.TpsLimitStrategy" is needed to implement. In this example, the strategy is implemented as randomly rejecting the incoming request. Examples are linked as follows: https://github.com/apache/dubbo-go-samples/tree/master/filter/tpslimit/go-server/pkg/limit_strategy.go
+
+B) Customize execution handler when the request is rejected.
+
+Implement the interface "filter.RejectedExecutionHandler" to customize the return result to the client when the request is rejected. In this example, when the TPS limit criteria meets, the customized execution handler will return the error "The request is rejected and doesn't have any default value." back to the consumer. Examples are linked as follows: https://github.com/apache/dubbo-go-samples/tree/master/filter/tpslimit/go-server/pkg/reject_handler.go
+
+##### 2. Configuration
+
+Enable tpslimit filter in provider's configuration file like below:
+
+```yaml
+# service config
+services:
+  UserProvider:
+    registry: demoZk
+    protocol: dubbo
+    interface: org.apache.dubbo.UserProvider
+    tps.limiter: method-service
+    tps.limit.strategy: RandomLimitStrategy
+    tps.limit.rejected.handler: DefaultValueHandler
+    tps.limit.interval: 5000
+    tps.limit.rate: 300
+```
+
+##### 3. Run
+
+Pls. refer to [HOWTO.md](../../HOWTO.md) under the root directory to run this sample.
+
+The provider side will print out:
+
+```bash
+[2021-03-10/17:11:10 github.com/apache/dubbo-go-samples/filter/tpslimit/go-server/pkg.RandomTpsLimitStrategy.IsAllowable: limit_strategy.go: 56] %s
+Random IsAllowable!
+2021-03-10T17:11:10.748+0800 ERROR   filter_impl/tps_limit_filter.go:69      The invocation was rejected due to over the tps limitation, ...
+```
+
+The consumer side will print out:
+
+```bash
+error: The request is rejected and doesn't have any default value. 
+```
\ No newline at end of file
diff --git a/filter/tpslimit/README_zh.md b/filter/tpslimit/README_zh.md
new file mode 100644
index 0000000..b2f0f21
--- /dev/null
+++ b/filter/tpslimit/README_zh.md
@@ -0,0 +1,53 @@
+# TPSLimit Filter 示例
+
+### 背景
+
+Dubbo-go 内置了限流 filter "tpslimit"。可以通过在服务端的配置来激活,另外,用户还可以自定义限流策略和拒绝访问后的处理逻辑。
+
+### 示例
+
+##### 1. 代码
+
+A) 自定义限流策略:
+
+通过实现 filter.TpsLimitStrategy 来自定义限流策略。在本例中,采取的策略是随机限流。例子链接为:https://github.com/apache/dubbo-go-samples/tree/master/filter/tpslimit/go-server/pkg/limit_strategy.go
+
+B) 自定义拒绝访问处理:
+
+通过实现 filter.RejectedExecutionHandler。在本例中,当限流条件满足的情况下,拒绝访问的自定义处理逻辑将会返回 "The request is rejected and doesn't have any default value. " 的错误给客户端。例子链接为:https://github.com/apache/dubbo-go-samples/tree/master/filter/tpslimit/go-server/pkg/reject_handler.go
+
+##### 2. 配置
+
+在服务端的配置文件中,按如下所示配置该 filter:
+
+```yaml
+# service config
+services:
+  UserProvider:
+    registry: demoZk
+    protocol: dubbo
+    interface: org.apache.dubbo.UserProvider
+    tps.limiter: method-service
+    tps.limit.strategy: RandomLimitStrategy
+    tps.limit.rejected.handler: DefaultValueHandler
+    tps.limit.interval: 5000
+    tps.limit.rate: 300
+```
+
+##### 3. 运行
+
+请参阅根目录中的 [HOWTO.md](../../HOWTO_zh.md) 来运行本例。
+
+观察服务端的输出:
+
+```bash
+[2021-03-10/17:11:10 github.com/apache/dubbo-go-samples/filter/tpslimit/go-server/pkg.RandomTpsLimitStrategy.IsAllowable: limit_strategy.go: 56] %s
+Random IsAllowable!
+2021-03-10T17:11:10.748+0800 ERROR   filter_impl/tps_limit_filter.go:69      The invocation was rejected due to over the tps limitation, ...
+```
+
+观察客户端的输出:
+
+```bash
+error: The request is rejected and doesn't have any default value. 
+```
\ No newline at end of file
diff --git a/filter/tpslimit/go-client/cmd/client.go b/filter/tpslimit/go-client/cmd/client.go
new file mode 100644
index 0000000..620c545
--- /dev/null
+++ b/filter/tpslimit/go-client/cmd/client.go
@@ -0,0 +1,64 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package main
+
+import (
+	"context"
+	"time"
+)
+
+import (
+	"dubbo.apache.org/dubbo-go/v3/common/logger"
+	"dubbo.apache.org/dubbo-go/v3/config"
+	_ "dubbo.apache.org/dubbo-go/v3/imports"
+
+	hessian "github.com/apache/dubbo-go-hessian2"
+)
+
+import (
+	"github.com/apache/dubbo-go-samples/filter/tpslimit/go-client/pkg"
+)
+
+var userProvider = &pkg.UserProvider{}
+
+func init() {
+	config.SetConsumerService(userProvider)
+	hessian.RegisterPOJO(&pkg.User{})
+}
+
+func main() {
+	err := config.Load()
+	if err != nil {
+		panic(err)
+	}
+
+	var successCount, failCount int64
+	logger.Infof("\n\n\nstart to test dubbo")
+	for i := 0; i < 60; i++ {
+		time.Sleep(200 * time.Millisecond)
+		user, err := userProvider.GetUser(context.TODO(), "A001")
+		if err != nil {
+			failCount++
+			logger.Infof("error: %v\n", err)
+		} else {
+			successCount++
+		}
+		logger.Infof("response: %v\n", user)
+	}
+	logger.Infof("failCount=%v, failCount=%v\n", successCount, failCount)
+}
diff --git a/tracing/jsonrpc/go-client/conf/dubbogo.yml b/filter/tpslimit/go-client/conf/dubbogo.yml
similarity index 58%
copy from tracing/jsonrpc/go-client/conf/dubbogo.yml
copy to filter/tpslimit/go-client/conf/dubbogo.yml
index d3cce0a..7b72cbf 100644
--- a/tracing/jsonrpc/go-client/conf/dubbogo.yml
+++ b/filter/tpslimit/go-client/conf/dubbogo.yml
@@ -7,12 +7,13 @@ dubbo:
       timeout: 3s
       address: 127.0.0.1:2181
   consumer:
-    filter: tracing
+    check: true
+    request_timeout: 3s
+    connect_timeout: 3s
     registryIDs:
       - demoZK
     references:
       UserProvider:
-        protocol: jsonrpc
-        interface: org.apache.dubbo.UserProvider
-        cluster: failover
-        
\ No newline at end of file
+        retries: 0
+        protocol: dubbo
+        interface: org.apache.dubbo.UserProvider
\ No newline at end of file
diff --git a/filter/tpslimit/go-client/pkg/user.go b/filter/tpslimit/go-client/pkg/user.go
new file mode 100644
index 0000000..505abac
--- /dev/null
+++ b/filter/tpslimit/go-client/pkg/user.go
@@ -0,0 +1,38 @@
+/*
+ * 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 pkg
+
+import (
+	"context"
+	"time"
+)
+
+type User struct {
+	ID   string
+	Name string
+	Age  int32
+	Time time.Time
+}
+
+type UserProvider struct {
+	GetUser func(ctx context.Context, req string) (*User, error)
+}
+
+func (u *User) JavaClassName() string {
+	return "org.apache.dubbo.User"
+}
diff --git a/filter/tpslimit/go-server/cmd/server.go b/filter/tpslimit/go-server/cmd/server.go
new file mode 100644
index 0000000..21243ab
--- /dev/null
+++ b/filter/tpslimit/go-server/cmd/server.go
@@ -0,0 +1,72 @@
+/*
+ * 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 (
+	"os"
+	"os/signal"
+	"syscall"
+	"time"
+)
+
+import (
+	"dubbo.apache.org/dubbo-go/v3/common/logger"
+	"dubbo.apache.org/dubbo-go/v3/config"
+	_ "dubbo.apache.org/dubbo-go/v3/filter/tps/strategy"
+	_ "dubbo.apache.org/dubbo-go/v3/imports"
+
+	hessian "github.com/apache/dubbo-go-hessian2"
+)
+
+import (
+	"github.com/apache/dubbo-go-samples/filter/tpslimit/go-server/pkg"
+)
+
+var (
+	survivalTimeout = int(3e9)
+)
+
+func main() {
+	config.SetProviderService(new(pkg.UserProvider))
+	hessian.RegisterPOJO(&pkg.User{})
+	config.Load()
+	initSignal()
+}
+
+func initSignal() {
+	signals := make(chan os.Signal, 1)
+	// It is not possible to block SIGKILL or syscall.SIGSTOP
+	signal.Notify(signals, os.Interrupt, os.Kill, syscall.SIGHUP, syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGINT)
+	for {
+		sig := <-signals
+		logger.Infof("get signal %s", sig.String())
+		switch sig {
+		case syscall.SIGHUP:
+			// reload()
+		default:
+			time.AfterFunc(time.Duration(survivalTimeout), func() {
+				logger.Warnf("app exit now by force...")
+				os.Exit(1)
+			})
+
+			// The program exits normally or timeout forcibly exits.
+			logger.Infof("provider app exit now...")
+			return
+		}
+	}
+}
diff --git a/filter/tpslimit/go-server/conf/dubbogo.yml b/filter/tpslimit/go-server/conf/dubbogo.yml
new file mode 100644
index 0000000..b48dc6b
--- /dev/null
+++ b/filter/tpslimit/go-server/conf/dubbogo.yml
@@ -0,0 +1,29 @@
+# dubbo server yaml configure file
+
+
+dubbo:
+  registries:
+    demoZK:
+      protocol: zookeeper
+      timeout: 3s
+      address: 127.0.0.1:2181
+  protocols:
+    dubbo:
+      name: dubbo
+      port: 20000
+  provider:
+    registryIDs:
+      - demoZK
+    services:
+      UserProvider:
+        protocol: dubbo
+        interface: org.apache.dubbo.UserProvider
+        cluster: failover
+        loadbalance: random # load balancing strategy, such as random, roundrobin, leastactive or consistenthash.
+        warmup: 100  # warmup period, in seconds
+        tps.limiter: method-service # the Limiter that judge if the TPS overs the threshold, such as method-service or default
+        tps.limit.strategy: fixedWindow # the name of limit strategy, such as fixedWindow, slidingWindow, default, threadSafeFixedWindow or the strategy name you customed
+        tps.limit.rejected.handler: DefaultValueHandler
+        tps.limit.interval: 1000 # the interval time unit is ms
+        tps.limit.rate: 3 # the max value in the interval. <0 means that the service will not be limited.
+        
diff --git a/filter/tpslimit/go-server/pkg/limit_strategy.go b/filter/tpslimit/go-server/pkg/limit_strategy.go
new file mode 100644
index 0000000..07a8651
--- /dev/null
+++ b/filter/tpslimit/go-server/pkg/limit_strategy.go
@@ -0,0 +1,78 @@
+/*
+ * 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 pkg
+
+import (
+	"math/rand"
+)
+
+import (
+	"dubbo.apache.org/dubbo-go/v3/common/extension"
+	"dubbo.apache.org/dubbo-go/v3/filter"
+
+	"github.com/dubbogo/gost/log"
+)
+
+func init() {
+	/*
+	 * register your implementation and them using it like:
+	 *
+	 * UserProvider:
+	 *   registry: hangzhouzk
+	 *   protocol : dubbo
+	 *   interface : com.ikurento.user.UserProvider
+	 *   ... # other configuration
+	 *   tps.limiter: method-service # the name of limiter
+	 *   tps.limit.strategy: RandomLimitStrategy
+	 */
+	extension.SetTpsLimitStrategy("RandomLimitStrategy", &RandomTpsLimitStrategyCreator{})
+}
+
+/**
+ * The RandomTpsLimitStrategy should not be singleton because different TpsLimiter will create many instances.
+ * we won't want them affect each other.
+ */
+type RandomTpsLimitStrategy struct {
+	rate     int
+	interval int
+}
+
+/**
+ * It implements the TpsLimitStrategy interface.
+ * IsAllowable will return true if this invocation is not over limitation.
+ */
+func (r RandomTpsLimitStrategy) IsAllowable() bool {
+	// this is a simple demo.
+	gxlog.CInfo("Random IsAllowable!")
+	randNum := rand.Int63n(2)
+	return randNum == 0
+}
+
+type RandomTpsLimitStrategyCreator struct{}
+
+/**
+ * It implements the TpsLimitStrategyCreator interface.
+ * TpsLimitStrategyCreator is the creator abstraction for TpsLimitStrategy.
+ * Create will create an instance of TpsLimitStrategy.
+ */
+func (creator *RandomTpsLimitStrategyCreator) Create(rate int, interval int) filter.TpsLimitStrategy {
+	return &RandomTpsLimitStrategy{
+		rate:     rate,
+		interval: interval,
+	}
+}
diff --git a/filter/tpslimit/go-server/pkg/reject_handler.go b/filter/tpslimit/go-server/pkg/reject_handler.go
new file mode 100644
index 0000000..60ccdc1
--- /dev/null
+++ b/filter/tpslimit/go-server/pkg/reject_handler.go
@@ -0,0 +1,107 @@
+/*
+ * 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 pkg
+
+import (
+	"errors"
+	"sync"
+)
+
+import (
+	"dubbo.apache.org/dubbo-go/v3/common"
+	"dubbo.apache.org/dubbo-go/v3/common/extension"
+	"dubbo.apache.org/dubbo-go/v3/common/logger"
+	"dubbo.apache.org/dubbo-go/v3/filter"
+	"dubbo.apache.org/dubbo-go/v3/protocol"
+)
+
+func init() {
+	/*
+	 * register your custom implementation into filter.
+	 * "DefaultValueHandler" is the name used in configure file, like server.yml:
+	 * UserProvider:
+	 *   registry: hangzhouzk
+	 *   protocol : dubbo
+	 *   interface : com.ikurento.user.UserProvider
+	 *   ... # other configuration
+	 *   tps.limiter: method-service,
+	 *
+	 *   tps.limit.rejected.handler: DefaultValueHandler,
+	 * So when the invocation is over the tps limitation, it will return the default value.
+	 * This is a common use case.
+	 */
+	extension.SetRejectedExecutionHandler("DefaultValueHandler", GetDefaultValueRejectedExecutionHandlerSingleton)
+
+}
+
+/**
+ * The RejectedExecutionHandler is used by some components,
+ * e.g, ExecuteLimitFilter, GracefulShutdownFilter, TpsLimitFilter.
+ * When the requests are rejected, the RejectedExecutionHandler allows you to do something.
+ * You can alert the developer, or redirect those requests to another providers. It depends on what you need.
+ *
+ * Let's assume that you need a RejectedExecutionHandler which will return some default result if the request was rejected.
+ */
+type DefaultValueRejectedExecutionHandler struct {
+	defaultResult sync.Map
+}
+
+func (mh *DefaultValueRejectedExecutionHandler) RejectedExecution(url *common.URL, invocation protocol.Invocation) protocol.Result {
+	// put your custom business here.
+	logger.Error("Here is my custom rejected handler. I want to do something if the requests are rejected. ")
+	// in most cases, if the request was rejected, you won't want to invoke the origin provider.
+	// But if you really want to do that, you can do it like this:
+	// invocation.Invoker().Invoke(invocation)
+
+	// the ServiceKey + methodName is the key
+	key := url.ServiceKey() + "#" + invocation.MethodName()
+	result, loaded := mh.defaultResult.Load(key)
+	if !loaded {
+		// we didn't configure any default value for this invocation
+		return &protocol.RPCResult{
+			Err: errors.New("The request is rejected and doesn't have any default value. "),
+		}
+	}
+	return result.(*protocol.RPCResult)
+}
+
+func GetCustomRejectedExecutionHandler() filter.RejectedExecutionHandler {
+	return &DefaultValueRejectedExecutionHandler{}
+}
+
+var (
+	customHandlerOnce     sync.Once
+	customHandlerInstance *DefaultValueRejectedExecutionHandler
+)
+
+/**
+ * the better way is designing the RejectedExecutionHandler as singleton.
+ */
+func GetDefaultValueRejectedExecutionHandlerSingleton() filter.RejectedExecutionHandler {
+	customHandlerOnce.Do(func() {
+		customHandlerInstance = &DefaultValueRejectedExecutionHandler{}
+	})
+
+	initDefaultValue()
+
+	return customHandlerInstance
+}
+
+func initDefaultValue() {
+	// setting your default value
+}
diff --git a/filter/tpslimit/go-server/pkg/user.go b/filter/tpslimit/go-server/pkg/user.go
new file mode 100644
index 0000000..aab906b
--- /dev/null
+++ b/filter/tpslimit/go-server/pkg/user.go
@@ -0,0 +1,48 @@
+/*
+ * 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 pkg
+
+import (
+	"context"
+	"time"
+)
+
+import (
+	"dubbo.apache.org/dubbo-go/v3/common/logger"
+)
+
+type User struct {
+	ID   string
+	Name string
+	Age  int32
+	Time time.Time
+}
+
+type UserProvider struct {
+}
+
+func (u *UserProvider) GetUser(ctx context.Context, req string) (*User, error) {
+	logger.Infof("req:%#v", req)
+	rsp := User{"A001", "Alex Stocks", 18, time.Now()}
+	logger.Infof("rsp:%#v", rsp)
+	return &rsp, nil
+}
+
+func (u *User) JavaClassName() string {
+	return "org.apache.dubbo.User"
+}
diff --git a/filter/tpslimit/go-server/tests/integration/main_test.go b/filter/tpslimit/go-server/tests/integration/main_test.go
new file mode 100644
index 0000000..2f1acc2
--- /dev/null
+++ b/filter/tpslimit/go-server/tests/integration/main_test.go
@@ -0,0 +1,61 @@
+// +build integration
+
+/*
+ * 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 integration
+
+import (
+	"context"
+	"os"
+	"testing"
+	"time"
+)
+
+import (
+	"dubbo.apache.org/dubbo-go/v3/config"
+	_ "dubbo.apache.org/dubbo-go/v3/imports"
+
+	hessian "github.com/apache/dubbo-go-hessian2"
+)
+
+var userProvider = new(UserProvider)
+
+func TestMain(m *testing.M) {
+	config.SetConsumerService(userProvider)
+	hessian.RegisterPOJO(&User{})
+	config.Load()
+
+	time.Sleep(3 * time.Second)
+
+	os.Exit(m.Run())
+}
+
+type User struct {
+	ID   string
+	Name string
+	Age  int32
+	Time time.Time
+}
+
+type UserProvider struct {
+	GetUser func(ctx context.Context, req string) (*User, error)
+}
+
+func (u *User) JavaClassName() string {
+	return "org.apache.dubbo.User"
+}
diff --git a/filter/tpslimit/go-server/tests/integration/userprovider_test.go b/filter/tpslimit/go-server/tests/integration/userprovider_test.go
new file mode 100644
index 0000000..50bd643
--- /dev/null
+++ b/filter/tpslimit/go-server/tests/integration/userprovider_test.go
@@ -0,0 +1,50 @@
+// +build integration
+
+/*
+ * 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 integration
+
+import (
+	"context"
+	"strings"
+	"testing"
+)
+
+import (
+	"github.com/stretchr/testify/assert"
+)
+
+func TestGetUser(t *testing.T) {
+	user := &User{}
+	var err error
+
+	// loop 50 times to make sure rejection happens
+	for i := 0; i < 50; i++ {
+		if user, err = userProvider.GetUser(context.TODO(), "A001"); err != nil {
+			break
+		}
+
+		assert.Equal(t, "A001", user.ID)
+		assert.Equal(t, "Alex Stocks", user.Name)
+		assert.Equal(t, int32(18), user.Age)
+		assert.NotNil(t, user.Time)
+	}
+
+	assert.NotNil(t, err)
+	assert.True(t, strings.Contains(err.Error(), "rejected"))
+}
diff --git a/tracing/jsonrpc/go-client/conf/dubbogo.yml b/tracing/jsonrpc/go-client/conf/dubbogo.yml
index d3cce0a..8cd0dd7 100644
--- a/tracing/jsonrpc/go-client/conf/dubbogo.yml
+++ b/tracing/jsonrpc/go-client/conf/dubbogo.yml
@@ -12,7 +12,7 @@ dubbo:
       - demoZK
     references:
       UserProvider:
-        protocol: jsonrpc
+        protocolIDs: jsonrpc
         interface: org.apache.dubbo.UserProvider
         cluster: failover
         
\ No newline at end of file