You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@dubbo.apache.org by xu...@apache.org on 2026/04/13 04:03:52 UTC

(dubbo-go-samples) branch main updated: feat: add router static config sample (#1059)

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

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


The following commit(s) were added to refs/heads/main by this push:
     new 29a15654 feat: add router static config sample (#1059)
29a15654 is described below

commit 29a1565416a7c6be9259ac55c453d0e4695875e1
Author: Aether <98...@users.noreply.github.com>
AuthorDate: Mon Apr 13 12:03:46 2026 +0800

    feat: add router static config sample (#1059)
    
    * add router static config sample
    
    * fix(router): exit non-zero when static config consumer invoke fails
    
    * refactor(router): align static router samples with integration layout
    
    * chore(router): add static router samples to integration docs and CI list
    
    * fix: add more attemps to reduce test flakiness
    
    * fix(router): align static tag router key with provider application
    
    ---------
    
    Co-authored-by: Aetherance <in...@gmail.com>
---
 README.md                                          |   2 +
 README_CN.md                                       |   2 +
 router/static_config/README.md                     |  27 ++++++
 router/static_config/README_CN.md                  |  27 ++++++
 router/static_config/condition/README.md           |  59 ++++++++++++
 router/static_config/condition/README_CN.md        |  59 ++++++++++++
 .../condition/go-client/cmd/client.go              |  94 ++++++++++++++++++
 .../condition/go-node2-server/cmd/server_node2.go  |  76 +++++++++++++++
 .../condition/go-server/cmd/server.go              |  76 +++++++++++++++
 router/static_config/tag/README.md                 |  78 +++++++++++++++
 router/static_config/tag/README_CN.md              |  78 +++++++++++++++
 router/static_config/tag/go-client/cmd/client.go   | 105 +++++++++++++++++++++
 router/static_config/tag/go-server/cmd/server.go   |  76 +++++++++++++++
 .../tag/go-tag-server/cmd/server_tag.go            |  77 +++++++++++++++
 start_integrate_test.sh                            |   2 +
 15 files changed, 838 insertions(+)

diff --git a/README.md b/README.md
index c7313344..4a30dc51 100644
--- a/README.md
+++ b/README.md
@@ -57,6 +57,8 @@ Please refer to [HOWTO.md](HOWTO.md) for detailed instructions on running the sa
   * `router/condition`: Dubbo-go condition router examples.
   * `router/script`: Dubbo-go script router examples.
   * `router/polaris`: Quickly experience Polaris' service routing capabilities in Dubbo-go.
+  * `router/static_config/tag`: Dubbo-go static tag router example.
+  * `router/static_config/condition`: Dubbo-go static condition router example.
 * `rpc`: Various RPC protocol examples with Dubbo-go.
   * `rpc/dubbo`: Dubbo protocol example, including Java–Go interop.
   * `rpc/grpc`: gRPC protocol example.
diff --git a/README_CN.md b/README_CN.md
index 943a0dcc..f5f4bebd 100644
--- a/README_CN.md
+++ b/README_CN.md
@@ -57,6 +57,8 @@
   * `router/condition`: Dubbo-go 的 condition router 使用示例。
   * `router/script`: Dubbo-go 的 script router 使用示例。
   * `router/polaris`:在 Dubbo-go 中快速体验北极星的服务路由能力。
+  * `router/static_config/tag`: 静态配置的 tag router 示例。
+  * `router/static_config/condition`: 静态配置的 condition router 示例。
 * `rpc`:Dubbo-go 支持的多种 RPC 协议示例。
   * `rpc/dubbo`:Dubbo 协议示例,包含 Java–Go 互操作。
   * `rpc/grpc`:基于 gRPC 协议的示例。
diff --git a/router/static_config/README.md b/router/static_config/README.md
new file mode 100644
index 00000000..56b52b02
--- /dev/null
+++ b/router/static_config/README.md
@@ -0,0 +1,27 @@
+# Static router configuration
+
+This directory contains static router samples configured directly in code.
+Here, `static_config` means the router rules are injected statically on the consumer side, instead of being delivered dynamically from a config center.
+
+English | [中文](README_CN.md)
+
+## Prerequisites
+
+- Go 1.25+.
+
+## Sub-samples
+
+- `condition`: service-scope static condition router sample with direct URLs
+- `tag`: application-scope static tag router sample with direct URLs
+
+## How to use
+
+Choose one sub-sample and follow its own README:
+
+- `condition/README.md`
+- `tag/README.md`
+
+The `condition` sample uses direct URLs only.
+The `tag` sample uses direct URLs only.
+Neither sample requires a config center.
+Each sub-sample can be run locally after following its own prerequisites and setup steps.
diff --git a/router/static_config/README_CN.md b/router/static_config/README_CN.md
new file mode 100644
index 00000000..f6a9707b
--- /dev/null
+++ b/router/static_config/README_CN.md
@@ -0,0 +1,27 @@
+# 静态路由配置
+
+这个目录用于展示如何在代码中直接配置 Dubbo-Go 的静态路由。
+这里的 `static_config` 指的是:在 consumer 侧通过代码静态注入路由规则,而不是从配置中心动态下发。
+
+[English](README.md) | 中文
+
+## 前置准备
+
+- Go 1.25+。
+
+## 子示例
+
+- `condition`:基于直连地址的服务级静态条件路由示例
+- `tag`:基于直连地址的应用级静态标签路由示例
+
+## 如何使用
+
+进入对应子目录,按各自 README 运行:
+
+- `condition/README_CN.md`
+- `tag/README_CN.md`
+
+`condition` 示例只使用直连 URL。
+`tag` 示例只使用直连 URL。
+两个示例都不需要配置中心。
+每个子示例都需要先完成各自 README 中的前置准备和启动步骤。
diff --git a/router/static_config/condition/README.md b/router/static_config/condition/README.md
new file mode 100644
index 00000000..3aebac2e
--- /dev/null
+++ b/router/static_config/condition/README.md
@@ -0,0 +1,59 @@
+# Static condition router
+
+This example shows how to configure Dubbo-Go's condition router statically in code with direct provider URLs.
+
+English | [中文](README_CN.md)
+
+## Prerequisites
+
+- Go 1.25+.
+
+## What this sample demonstrates
+
+- Two providers listening directly on ports `20000` and `20001`
+- A service-scope static condition router configured by `dubbo.WithRouter(...)`
+- A `force=true` rule that routes `Greet` requests to port `20000`
+
+## How to run
+
+### Run providers
+
+Start two providers in separate terminals:
+
+```shell
+$ go run ./go-server/cmd/server.go
+$ go run ./go-node2-server/cmd/server_node2.go
+```
+
+- `go-server` listens on `:20000`
+- `go-node2-server` listens on `:20001`
+
+### Run consumer
+
+If you want to change the provider addresses, update `directURL` in `go-client/cmd/client.go`.
+
+```shell
+$ go run ./go-client/cmd/client.go
+```
+
+The client connects to both providers by direct URLs.
+No registry or config center is required.
+
+## Expected result
+
+- The client logs `invoke successfully: receive: static condition router, response from: server-node-20000`
+
+## Key router config
+
+The consumer injects this service-scope static condition router:
+
+```go
+dubbo.WithRouter(
+    router.WithScope("service"),
+    router.WithKey(greet.GreetServiceName),
+    router.WithForce(true),
+    router.WithConditions([]string{
+        "method = Greet => port = 20000",
+    }),
+)
+```
diff --git a/router/static_config/condition/README_CN.md b/router/static_config/condition/README_CN.md
new file mode 100644
index 00000000..3a5794d7
--- /dev/null
+++ b/router/static_config/condition/README_CN.md
@@ -0,0 +1,59 @@
+# 静态条件路由
+
+这个示例展示了如何在代码中直接配置 Dubbo-Go 的静态条件路由,并通过直连地址访问服务实例。
+
+[English](README.md) | 中文
+
+## 前置准备
+
+- Go 1.25+。
+
+## 这个示例演示了什么
+
+- 两个通过直连地址访问的 provider,分别监听 `20000` 和 `20001`
+- 使用 `dubbo.WithRouter(...)` 配置服务级静态 condition router
+- 通过 `force=true` 规则把 `Greet` 请求固定路由到 `20000`
+
+## 如何运行
+
+### 启动 Provider
+
+在两个终端中分别启动下面两个 provider:
+
+```shell
+$ go run ./go-server/cmd/server.go
+$ go run ./go-node2-server/cmd/server_node2.go
+```
+
+- `go-server` 监听 `:20000`
+- `go-node2-server` 监听 `:20001`
+
+### 启动 Consumer
+
+如果你需要修改 provider 地址,请同步修改 `go-client/cmd/client.go` 中的 `directURL`。
+
+```shell
+$ go run ./go-client/cmd/client.go
+```
+
+客户端会通过直连 URL 连接两个 provider。
+这个示例不需要注册中心和配置中心。
+
+## 预期结果
+
+- 客户端会打印 `invoke successfully: receive: static condition router, response from: server-node-20000`
+
+## 关键路由配置
+
+consumer 注入的是一个服务级静态 condition router:
+
+```go
+dubbo.WithRouter(
+    router.WithScope("service"),
+    router.WithKey(greet.GreetServiceName),
+    router.WithForce(true),
+    router.WithConditions([]string{
+        "method = Greet => port = 20000",
+    }),
+)
+```
diff --git a/router/static_config/condition/go-client/cmd/client.go b/router/static_config/condition/go-client/cmd/client.go
new file mode 100644
index 00000000..f0936b8f
--- /dev/null
+++ b/router/static_config/condition/go-client/cmd/client.go
@@ -0,0 +1,94 @@
+/*
+ * 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"
+	"fmt"
+	"os"
+	"strings"
+)
+
+import (
+	"dubbo.apache.org/dubbo-go/v3"
+	"dubbo.apache.org/dubbo-go/v3/client"
+	"dubbo.apache.org/dubbo-go/v3/cluster/router"
+	_ "dubbo.apache.org/dubbo-go/v3/imports"
+
+	"github.com/dubbogo/gost/log/logger"
+)
+
+import (
+	greet "github.com/apache/dubbo-go-samples/direct/proto"
+)
+
+const (
+	clientApplication = "static-condition-client"
+	directURL         = "tri://127.0.0.1:20000;tri://127.0.0.1:20001"
+	expectedServer    = "server-node-20000"
+	attemptCount      = 5
+)
+
+func main() {
+	ins, err := dubbo.NewInstance(
+		dubbo.WithName(clientApplication),
+		dubbo.WithRouter(
+			router.WithScope("service"),
+			router.WithKey(greet.GreetServiceName),
+			router.WithPriority(100),
+			router.WithForce(true),
+			router.WithConditions([]string{
+				"method = Greet => port = 20000",
+			}),
+		),
+	)
+	if err != nil {
+		logger.Errorf("new instance failed: %v", err)
+		panic(err)
+	}
+
+	cli, err := ins.NewClient(client.WithClientURL(directURL))
+	if err != nil {
+		logger.Errorf("new client failed: %v", err)
+		panic(err)
+	}
+
+	svc, err := greet.NewGreetService(cli)
+	if err != nil {
+		logger.Errorf("new service failed: %v", err)
+		panic(err)
+	}
+
+	for i := 1; i <= attemptCount; i++ {
+		resp, err := svc.Greet(context.Background(), &greet.GreetRequest{
+			Name: fmt.Sprintf("static condition router attempt %d", i),
+		})
+		if err != nil {
+			logger.Errorf("invoke failed on attempt %d/%d: %v", i, attemptCount, err)
+			os.Exit(1)
+		}
+
+		if !strings.Contains(resp.Greeting, expectedServer) {
+			logger.Errorf("routed to unexpected server on attempt %d/%d, want %s, got %q",
+				i, attemptCount, expectedServer, resp.Greeting)
+			os.Exit(1)
+		}
+
+		logger.Infof("invoke successfully on attempt %d/%d: %v", i, attemptCount, resp.Greeting)
+	}
+}
diff --git a/router/static_config/condition/go-node2-server/cmd/server_node2.go b/router/static_config/condition/go-node2-server/cmd/server_node2.go
new file mode 100644
index 00000000..c723d78e
--- /dev/null
+++ b/router/static_config/condition/go-node2-server/cmd/server_node2.go
@@ -0,0 +1,76 @@
+/*
+ * 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"
+)
+
+import (
+	"dubbo.apache.org/dubbo-go/v3"
+	_ "dubbo.apache.org/dubbo-go/v3/imports"
+	"dubbo.apache.org/dubbo-go/v3/protocol"
+
+	"github.com/dubbogo/gost/log/logger"
+)
+
+import (
+	greet "github.com/apache/dubbo-go-samples/direct/proto"
+)
+
+const (
+	providerApplication = "static-condition-provider"
+	triPort             = 20001
+	serverName          = "server-node-20001"
+)
+
+type GreetServer struct{}
+
+func (s *GreetServer) Greet(_ context.Context, req *greet.GreetRequest) (*greet.GreetResponse, error) {
+	logger.Infof("%s received request: %s", serverName, req.Name)
+	return &greet.GreetResponse{
+		Greeting: "receive: " + req.Name + ", response from: " + serverName,
+	}, nil
+}
+
+func main() {
+	ins, err := dubbo.NewInstance(
+		dubbo.WithName(providerApplication),
+		dubbo.WithProtocol(
+			protocol.WithTriple(),
+			protocol.WithPort(triPort),
+		),
+	)
+	if err != nil {
+		panic(err)
+	}
+
+	srv, err := ins.NewServer()
+	if err != nil {
+		panic(err)
+	}
+
+	if err := greet.RegisterGreetServiceHandler(srv, &GreetServer{}); err != nil {
+		panic(err)
+	}
+
+	logger.Infof("%s started on :%d", serverName, triPort)
+	if err := srv.Serve(); err != nil {
+		logger.Errorf("%s stopped: %v", serverName, err)
+	}
+}
diff --git a/router/static_config/condition/go-server/cmd/server.go b/router/static_config/condition/go-server/cmd/server.go
new file mode 100644
index 00000000..18582ac0
--- /dev/null
+++ b/router/static_config/condition/go-server/cmd/server.go
@@ -0,0 +1,76 @@
+/*
+ * 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"
+)
+
+import (
+	"dubbo.apache.org/dubbo-go/v3"
+	_ "dubbo.apache.org/dubbo-go/v3/imports"
+	"dubbo.apache.org/dubbo-go/v3/protocol"
+
+	"github.com/dubbogo/gost/log/logger"
+)
+
+import (
+	greet "github.com/apache/dubbo-go-samples/direct/proto"
+)
+
+const (
+	providerApplication = "static-condition-provider"
+	triPort             = 20000
+	serverName          = "server-node-20000"
+)
+
+type GreetServer struct{}
+
+func (s *GreetServer) Greet(_ context.Context, req *greet.GreetRequest) (*greet.GreetResponse, error) {
+	logger.Infof("%s received request: %s", serverName, req.Name)
+	return &greet.GreetResponse{
+		Greeting: "receive: " + req.Name + ", response from: " + serverName,
+	}, nil
+}
+
+func main() {
+	ins, err := dubbo.NewInstance(
+		dubbo.WithName(providerApplication),
+		dubbo.WithProtocol(
+			protocol.WithTriple(),
+			protocol.WithPort(triPort),
+		),
+	)
+	if err != nil {
+		panic(err)
+	}
+
+	srv, err := ins.NewServer()
+	if err != nil {
+		panic(err)
+	}
+
+	if err := greet.RegisterGreetServiceHandler(srv, &GreetServer{}); err != nil {
+		panic(err)
+	}
+
+	logger.Infof("%s started on :%d", serverName, triPort)
+	if err := srv.Serve(); err != nil {
+		logger.Errorf("%s stopped: %v", serverName, err)
+	}
+}
diff --git a/router/static_config/tag/README.md b/router/static_config/tag/README.md
new file mode 100644
index 00000000..10eb9f04
--- /dev/null
+++ b/router/static_config/tag/README.md
@@ -0,0 +1,78 @@
+# Static tag router
+
+This example shows how to configure Dubbo-Go's tag router statically in code, without a registry or config center.
+
+English | [中文](README_CN.md)
+
+## Prerequisites
+
+- Go 1.25+.
+
+## What this sample demonstrates
+
+- One untagged provider on port `20000`
+- One `gray` tagged provider on port `20002`
+- An application-scope static tag router configured by `dubbo.WithRouter(...)`
+
+## How to run
+
+### Run providers
+
+Start two providers in separate terminals:
+
+```shell
+$ go run ./go-server/cmd/server.go
+$ go run ./go-tag-server/cmd/server_tag.go
+```
+
+- `go-server` listens on `:20000` without a tag
+- `go-tag-server` listens on `:20002` and exports the service with tag `gray`
+
+### Run consumer
+
+```shell
+$ go run ./go-client/cmd/client.go
+```
+
+The client uses direct URLs only:
+
+```text
+tri://127.0.0.1:20000;tri://127.0.0.1:20002?dubbo.tag=gray
+```
+
+No registry or config center is required.
+
+## Expected result
+
+The client runs a single scenario and routes to `server-with-gray-tag`.
+You should see logs similar to:
+
+```text
+INFO ... invoke successfully: receive: static tag router, response from: server-with-gray-tag
+```
+
+## Key router config
+
+Static tag router:
+
+```go
+dubbo.WithRouter(
+    router.WithScope("application"),
+    router.WithKey("static-tag-provider"),
+    router.WithForce(false),
+    router.WithTags([]global.Tag{
+        {
+            Name:      "gray",
+            Addresses: []string{"127.0.0.1:20002"},
+        },
+    }),
+)
+```
+
+Request with tag attachment:
+
+```go
+ctx := context.WithValue(context.Background(), constant.AttachmentKey, map[string]string{
+    constant.Tagkey: "gray",
+})
+```
diff --git a/router/static_config/tag/README_CN.md b/router/static_config/tag/README_CN.md
new file mode 100644
index 00000000..ca357f7f
--- /dev/null
+++ b/router/static_config/tag/README_CN.md
@@ -0,0 +1,78 @@
+# 静态标签路由
+
+这个示例展示了如何在代码中直接配置 Dubbo-Go 的静态标签路由,不依赖注册中心和配置中心。
+
+[English](README.md) | 中文
+
+## 前置准备
+
+- Go 1.25+。
+
+## 这个示例演示了什么
+
+- 一个不带 tag 的 provider,监听 `20000`
+- 一个带 `gray` tag 的 provider,监听 `20002`
+- 使用 `dubbo.WithRouter(...)` 配置应用级静态 tag router
+
+## 如何运行
+
+### 启动 Provider
+
+在两个终端中分别启动下面两个 provider:
+
+```shell
+$ go run ./go-server/cmd/server.go
+$ go run ./go-tag-server/cmd/server_tag.go
+```
+
+- `go-server` 监听 `:20000`,不带 tag
+- `go-tag-server` 监听 `:20002`,并以 `gray` 标签导出服务
+
+### 启动 Consumer
+
+```shell
+$ go run ./go-client/cmd/client.go
+```
+
+客户端只使用直连 URL:
+
+```text
+tri://127.0.0.1:20000;tri://127.0.0.1:20002?dubbo.tag=gray
+```
+
+这个示例不需要注册中心,也不需要配置中心。
+
+## 预期结果
+
+客户端会运行一个场景,并路由到 `server-with-gray-tag`。
+你会看到类似下面的日志:
+
+```text
+INFO ... invoke successfully: receive: static tag router, response from: server-with-gray-tag
+```
+
+## 关键路由配置
+
+静态 tag router:
+
+```go
+dubbo.WithRouter(
+    router.WithScope("application"),
+    router.WithKey("static-tag-provider"),
+    router.WithForce(false),
+    router.WithTags([]global.Tag{
+        {
+            Name:      "gray",
+            Addresses: []string{"127.0.0.1:20002"},
+        },
+    }),
+)
+```
+
+携带请求 tag:
+
+```go
+ctx := context.WithValue(context.Background(), constant.AttachmentKey, map[string]string{
+    constant.Tagkey: "gray",
+})
+```
diff --git a/router/static_config/tag/go-client/cmd/client.go b/router/static_config/tag/go-client/cmd/client.go
new file mode 100644
index 00000000..931b706e
--- /dev/null
+++ b/router/static_config/tag/go-client/cmd/client.go
@@ -0,0 +1,105 @@
+/*
+ * 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"
+	"fmt"
+	"os"
+	"strings"
+)
+
+import (
+	"dubbo.apache.org/dubbo-go/v3"
+	"dubbo.apache.org/dubbo-go/v3/client"
+	"dubbo.apache.org/dubbo-go/v3/cluster/router"
+	"dubbo.apache.org/dubbo-go/v3/common/constant"
+	"dubbo.apache.org/dubbo-go/v3/global"
+	_ "dubbo.apache.org/dubbo-go/v3/imports"
+
+	"github.com/dubbogo/gost/log/logger"
+)
+
+import (
+	greet "github.com/apache/dubbo-go-samples/direct/proto"
+)
+
+const (
+	clientApplication = "static-tag-client"
+	tagName           = "gray"
+	grayAddress       = "127.0.0.1:20002"
+	directURL         = "tri://127.0.0.1:20000;tri://127.0.0.1:20002?dubbo.tag=gray"
+	expectedServer    = "server-with-gray-tag"
+	attemptCount      = 5
+)
+
+func main() {
+	ins, err := dubbo.NewInstance(
+		dubbo.WithName(clientApplication),
+		dubbo.WithRouter(
+			router.WithScope("application"),
+			router.WithKey("static-tag-provider"),
+			router.WithPriority(100),
+			router.WithForce(false),
+			router.WithTags([]global.Tag{
+				{
+					Name:      tagName,
+					Addresses: []string{grayAddress},
+				},
+			}),
+		),
+	)
+	if err != nil {
+		logger.Errorf("new instance failed: %v", err)
+		panic(err)
+	}
+
+	cli, err := ins.NewClient(client.WithClientURL(directURL))
+	if err != nil {
+		logger.Errorf("new client failed: %v", err)
+		panic(err)
+	}
+
+	svc, err := greet.NewGreetService(cli)
+	if err != nil {
+		logger.Errorf("new service failed: %v", err)
+		panic(err)
+	}
+
+	ctx := context.WithValue(context.Background(), constant.AttachmentKey, map[string]string{
+		constant.Tagkey: tagName,
+	})
+
+	for i := 1; i <= attemptCount; i++ {
+		resp, err := svc.Greet(ctx, &greet.GreetRequest{
+			Name: fmt.Sprintf("static tag router attempt %d", i),
+		})
+		if err != nil {
+			logger.Errorf("invoke failed on attempt %d/%d: %v", i, attemptCount, err)
+			os.Exit(1)
+		}
+
+		if !strings.Contains(resp.Greeting, expectedServer) {
+			logger.Errorf("routed to unexpected server on attempt %d/%d, want %s, got %q",
+				i, attemptCount, expectedServer, resp.Greeting)
+			os.Exit(1)
+		}
+
+		logger.Infof("invoke successfully on attempt %d/%d: %v", i, attemptCount, resp.Greeting)
+	}
+}
diff --git a/router/static_config/tag/go-server/cmd/server.go b/router/static_config/tag/go-server/cmd/server.go
new file mode 100644
index 00000000..12fa2f33
--- /dev/null
+++ b/router/static_config/tag/go-server/cmd/server.go
@@ -0,0 +1,76 @@
+/*
+ * 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"
+)
+
+import (
+	"dubbo.apache.org/dubbo-go/v3"
+	_ "dubbo.apache.org/dubbo-go/v3/imports"
+	"dubbo.apache.org/dubbo-go/v3/protocol"
+
+	"github.com/dubbogo/gost/log/logger"
+)
+
+import (
+	greet "github.com/apache/dubbo-go-samples/direct/proto"
+)
+
+const (
+	providerApplication = "static-tag-provider"
+	triPort             = 20000
+	serverName          = "server-without-tag"
+)
+
+type GreetServer struct{}
+
+func (s *GreetServer) Greet(_ context.Context, req *greet.GreetRequest) (*greet.GreetResponse, error) {
+	logger.Infof("%s received request: %s", serverName, req.Name)
+	return &greet.GreetResponse{
+		Greeting: "receive: " + req.Name + ", response from: " + serverName,
+	}, nil
+}
+
+func main() {
+	ins, err := dubbo.NewInstance(
+		dubbo.WithName(providerApplication),
+		dubbo.WithProtocol(
+			protocol.WithTriple(),
+			protocol.WithPort(triPort),
+		),
+	)
+	if err != nil {
+		panic(err)
+	}
+
+	srv, err := ins.NewServer()
+	if err != nil {
+		panic(err)
+	}
+
+	if err := greet.RegisterGreetServiceHandler(srv, &GreetServer{}); err != nil {
+		panic(err)
+	}
+
+	logger.Infof("%s started on :%d", serverName, triPort)
+	if err := srv.Serve(); err != nil {
+		logger.Errorf("%s stopped: %v", serverName, err)
+	}
+}
diff --git a/router/static_config/tag/go-tag-server/cmd/server_tag.go b/router/static_config/tag/go-tag-server/cmd/server_tag.go
new file mode 100644
index 00000000..e7a0faf6
--- /dev/null
+++ b/router/static_config/tag/go-tag-server/cmd/server_tag.go
@@ -0,0 +1,77 @@
+/*
+ * 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"
+)
+
+import (
+	"dubbo.apache.org/dubbo-go/v3"
+	_ "dubbo.apache.org/dubbo-go/v3/imports"
+	"dubbo.apache.org/dubbo-go/v3/protocol"
+	"dubbo.apache.org/dubbo-go/v3/server"
+
+	"github.com/dubbogo/gost/log/logger"
+)
+
+import (
+	greet "github.com/apache/dubbo-go-samples/direct/proto"
+)
+
+const (
+	providerApplication = "static-tag-provider"
+	triPort             = 20002
+	serverName          = "server-with-gray-tag"
+)
+
+type GreetServer struct{}
+
+func (s *GreetServer) Greet(_ context.Context, req *greet.GreetRequest) (*greet.GreetResponse, error) {
+	logger.Infof("%s received request: %s", serverName, req.Name)
+	return &greet.GreetResponse{
+		Greeting: "receive: " + req.Name + ", response from: " + serverName,
+	}, nil
+}
+
+func main() {
+	ins, err := dubbo.NewInstance(
+		dubbo.WithName(providerApplication),
+		dubbo.WithProtocol(
+			protocol.WithTriple(),
+			protocol.WithPort(triPort),
+		),
+	)
+	if err != nil {
+		panic(err)
+	}
+
+	srv, err := ins.NewServer()
+	if err != nil {
+		panic(err)
+	}
+
+	if err := greet.RegisterGreetServiceHandler(srv, &GreetServer{}, server.WithTag("gray")); err != nil {
+		panic(err)
+	}
+
+	logger.Infof("%s started on :%d with tag gray", serverName, triPort)
+	if err := srv.Serve(); err != nil {
+		logger.Errorf("%s stopped: %v", serverName, err)
+	}
+}
diff --git a/start_integrate_test.sh b/start_integrate_test.sh
index 7706ea76..91200bc9 100755
--- a/start_integrate_test.sh
+++ b/start_integrate_test.sh
@@ -97,6 +97,8 @@ array+=("java_interop/service_discovery/service")
 
 # router
 array+=("router/tag")
+array+=("router/static_config/tag")
+array+=("router/static_config/condition")
 
 DOCKER_DIR="$(pwd)"
 DOCKER_COMPOSE_CMD="docker-compose"