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 2025/10/19 14:01:03 UTC

(dubbo-go-samples) branch main updated: feat: add OTLP HTTP tracing exporter example with Dubbo/Triple/JsonRPC protocols (#926)

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

alexstocks 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 468ff5af feat: add OTLP HTTP tracing exporter example with Dubbo/Triple/JsonRPC protocols (#926)
468ff5af is described below

commit 468ff5afaf4c8f2047157600f16011098bf98918
Author: Xuetao Li <m1...@163.com>
AuthorDate: Sun Oct 19 22:00:59 2025 +0800

    feat: add OTLP HTTP tracing exporter example with Dubbo/Triple/JsonRPC protocols (#926)
    
    * feat: add OTLP HTTP tracing exporter example with Dubbo, Triple, and JSON-RPC protocols
---
 README.md                                          |   2 +
 README_CN.md                                       |   2 +
 go.mod                                             |   9 +-
 go.sum                                             |  19 +-
 .../tests/integration/otel_test.go                 | 216 ++++++++++++++++++
 otel/tracing/otlp_http_exporter/README.md          |  30 +++
 otel/tracing/otlp_http_exporter/README_CN.md       |  29 +++
 .../otlp_http_exporter/go-server/cmd/main.go       | 245 +++++++++++++++++++++
 start_integrate_test.sh                            |   1 +
 9 files changed, 535 insertions(+), 18 deletions(-)

diff --git a/README.md b/README.md
index cf5fb8bb..16a662c3 100644
--- a/README.md
+++ b/README.md
@@ -50,6 +50,8 @@
 * metrics: How to collect and expose metrics from Dubbo-go services, supporting both Prometheus Push and Pull modes , it also includes the tool (pgw-cleaner) used to clear zombie metrics in Push mode.
 * online_boutique: Microservices online boutique demo using Dubbo-go
 * otel/tracing: Distributed tracing example using OpenTelemetry
+  * stdout: use stdout exporter to export tracing data example
+  * otlp_http_exporter: use otlpHttpExporter to export tracing data example, including dubbo/triple/jsonrpc protocols
 * registry: Examples of using different service registries (e.g., nacos, zookeeper)
 * retry: Demonstrates retry mechanisms in Dubbo-go RPC calls
 * rpc: Various RPC protocol examples with Dubbo-go
diff --git a/README_CN.md b/README_CN.md
index 9fd65d67..0af628a3 100644
--- a/README_CN.md
+++ b/README_CN.md
@@ -46,6 +46,8 @@
 * metrics:Dubbo-go 服务指标采集与暴露示例,支持 Prometheus Push 模式和 Pull 模式, 同时包含 Push 模式下僵尸指标清理工具 “pgw-cleaner”
 * online_boutique:Dubbo-go 微服务电商示例
 * otel/tracing:基于 OpenTelemetry 的分布式链路追踪示例
+  * stdout: 使用 stdout exporter 输出追踪数据示例
+  * otlp_http_exporter: 使用 otlpHttpExporter 输出追踪数据示例,含 dubbo/triple/jsonrpc 协议
 * registry:多种服务注册中心(如 nacos、zookeeper)用法示例
 * retry:Dubbo-go RPC 调用重试机制示例
 * rpc:多种 RPC 协议示例
diff --git a/go.mod b/go.mod
index 7dcb669e..5c1592b2 100644
--- a/go.mod
+++ b/go.mod
@@ -23,7 +23,7 @@ require (
 	github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5
 	github.com/openzipkin/zipkin-go v0.4.2
 	github.com/pkg/errors v0.9.1
-	github.com/prometheus/client_golang v1.13.0
+	github.com/prometheus/client_golang v1.19.1
 	github.com/seata/seata-go v0.1.0-rc1
 	github.com/stretchr/testify v1.9.0
 	github.com/tmc/langchaingo v0.1.13
@@ -31,6 +31,7 @@ require (
 	go.opentelemetry.io/otel v1.26.0
 	go.opentelemetry.io/otel/exporters/jaeger v1.17.0
 	go.opentelemetry.io/otel/sdk v1.21.0
+	go.opentelemetry.io/proto/otlp v1.0.0
 	google.golang.org/grpc v1.64.0
 	google.golang.org/protobuf v1.34.1
 	gopkg.in/yaml.v3 v3.0.1
@@ -106,7 +107,6 @@ require (
 	github.com/magiconair/properties v1.8.7 // indirect
 	github.com/mattn/go-colorable v0.1.13 // indirect
 	github.com/mattn/go-isatty v0.0.20 // indirect
-	github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
 	github.com/mitchellh/copystructure v1.2.0 // indirect
 	github.com/mitchellh/go-homedir v1.1.0 // indirect
 	github.com/mitchellh/mapstructure v1.5.0 // indirect
@@ -125,8 +125,8 @@ require (
 	github.com/polarismesh/polaris-go v1.3.0 // indirect
 	github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
 	github.com/prometheus/client_model v0.5.0 // indirect
-	github.com/prometheus/common v0.37.0 // indirect
-	github.com/prometheus/procfs v0.8.0 // indirect
+	github.com/prometheus/common v0.48.0 // indirect
+	github.com/prometheus/procfs v0.12.0 // indirect
 	github.com/shirou/gopsutil/v3 v3.23.12 // indirect
 	github.com/shoenig/go-m1cpu v0.1.6 // indirect
 	github.com/sirupsen/logrus v1.9.3 // indirect
@@ -148,7 +148,6 @@ require (
 	go.opentelemetry.io/otel/exporters/zipkin v1.21.0 // indirect
 	go.opentelemetry.io/otel/metric v1.26.0 // indirect
 	go.opentelemetry.io/otel/trace v1.26.0 // indirect
-	go.opentelemetry.io/proto/otlp v1.0.0 // indirect
 	go.uber.org/atomic v1.10.0 // indirect
 	go.uber.org/multierr v1.8.0 // indirect
 	go.uber.org/zap v1.21.0 // indirect
diff --git a/go.sum b/go.sum
index 027bc322..1c9b0d3c 100644
--- a/go.sum
+++ b/go.sum
@@ -306,13 +306,11 @@ github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2
 github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
 github.com/go-kit/kit v0.10.0/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgOZ7o=
 github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
-github.com/go-kit/log v0.2.0/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0=
 github.com/go-ldap/ldap v3.0.2+incompatible/go.mod h1:qfd9rJvER9Q0/D/Sqn1DfHRoBp40uXYvFoEVrNEPqRc=
 github.com/go-ldap/ldap/v3 v3.1.10/go.mod h1:5Zun81jBTabRaI8lzN7E1JjyEl1g6zI6u9pd8luAK4Q=
 github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
 github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
 github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
-github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs=
 github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
 github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ=
 github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
@@ -646,8 +644,6 @@ github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWE
 github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
 github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
 github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
-github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo=
-github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
 github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
 github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso=
 github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI=
@@ -775,8 +771,8 @@ github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqr
 github.com/prometheus/client_golang v1.11.1/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0=
 github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY=
 github.com/prometheus/client_golang v1.12.2/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY=
-github.com/prometheus/client_golang v1.13.0 h1:b71QUfeo5M8gq2+evJdTPfZhYMAU0uKPkyPJ7TPsloU=
-github.com/prometheus/client_golang v1.13.0/go.mod h1:vTeo+zgvILHsnnj/39Ou/1fPN5nJFOEMgftOUOmlvYQ=
+github.com/prometheus/client_golang v1.19.1 h1:wZWJDwK+NameRJuPGDhlnFgx8e8HN3XHQeLaYJFJBOE=
+github.com/prometheus/client_golang v1.19.1/go.mod h1:mP78NwGzrVks5S2H6ab8+ZZGJLZUq1hoULYBAYBw1Ho=
 github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
 github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
 github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
@@ -796,8 +792,8 @@ github.com/prometheus/common v0.15.0/go.mod h1:U+gB1OBLb1lF3O42bTCL+FK18tX9Oar16
 github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc=
 github.com/prometheus/common v0.28.0/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls=
 github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls=
-github.com/prometheus/common v0.37.0 h1:ccBbHCgIiT9uSoFY0vX8H3zsNR5eLt17/RQLUvn8pXE=
-github.com/prometheus/common v0.37.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA=
+github.com/prometheus/common v0.48.0 h1:QO8U2CdOzSn1BBsmXJXduaaW+dY/5QLjfB8svtSzKKE=
+github.com/prometheus/common v0.48.0/go.mod h1:0/KsvlIEfPQCQ5I2iNSAWKPZziNCvRs5EC6ILDTlAPc=
 github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
 github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
 github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
@@ -807,8 +803,8 @@ github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4O
 github.com/prometheus/procfs v0.2.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
 github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
 github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
-github.com/prometheus/procfs v0.8.0 h1:ODq8ZFEaYeCaZOJlZZdJA2AbQR98dSHSM1KW/You5mo=
-github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4=
+github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo=
+github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo=
 github.com/prometheus/statsd_exporter v0.21.0/go.mod h1:rbT83sZq2V+p73lHhPZfMc3MLCHmSHelCh9hSGYNLTQ=
 github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
 github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
@@ -1136,8 +1132,6 @@ golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qx
 golang.org/x/net v0.0.0-20211029224645-99673261e6eb/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
 golang.org/x/net v0.0.0-20211105192438-b53810dc28af/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
 golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
-golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
-golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
 golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
 golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
 golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8=
@@ -1149,7 +1143,6 @@ golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4Iltr
 golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
 golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
 golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
-golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc=
 golang.org/x/oauth2 v0.27.0 h1:da9Vo7/tDv5RH/7nZDz1eMGS/q1Vv1N/7FCrBhI9I3M=
 golang.org/x/oauth2 v0.27.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8=
 golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
diff --git a/integrate_test/otel/tracing/otlp_http_exporter/tests/integration/otel_test.go b/integrate_test/otel/tracing/otlp_http_exporter/tests/integration/otel_test.go
new file mode 100644
index 00000000..6b7f9453
--- /dev/null
+++ b/integrate_test/otel/tracing/otlp_http_exporter/tests/integration/otel_test.go
@@ -0,0 +1,216 @@
+/*
+ * 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"
+	"fmt"
+	"net"
+	"os"
+	"testing"
+	"time"
+)
+
+import (
+	"dubbo.apache.org/dubbo-go/v3"
+	"dubbo.apache.org/dubbo-go/v3/client"
+	"dubbo.apache.org/dubbo-go/v3/common/constant"
+	_ "dubbo.apache.org/dubbo-go/v3/imports"
+	"dubbo.apache.org/dubbo-go/v3/otel/trace"
+
+	"github.com/dubbogo/gost/log/logger"
+)
+
+import (
+	greetpb "github.com/apache/dubbo-go-samples/otel/tracing/stdout/proto"
+)
+
+// ---- Test configuration ----
+const (
+	defaultOtelEndpoint = "127.0.0.1:4318"
+
+	tripleAddr  = "127.0.0.1:20000"
+	dubboAddr   = "127.0.0.1:20001"
+	jsonRPCAddr = "127.0.0.1:20002"
+
+	testTimeout   = 8 * time.Second
+	exporterGrace = 5 * time.Second // best-effort to allow OTLP export
+)
+
+var ins *dubbo.Instance
+
+// TestMain handles global setup/teardown once for all integration tests.
+func TestMain(m *testing.M) {
+	var err error
+
+	otel := os.Getenv("OTEL_EXPORTER_OTLP_ENDPOINT")
+	if otel == "" {
+		otel = defaultOtelEndpoint
+	}
+
+	ins, err = dubbo.NewInstance(
+		dubbo.WithName("dubbo_otel_client_it"),
+		dubbo.WithTracing(
+			trace.WithEnabled(),
+			trace.WithOtlpHttpExporter(), // OTLP/HTTP -> /v1/traces
+			trace.WithW3cPropagator(),
+			trace.WithAlwaysMode(),
+			trace.WithEndpoint(otel),
+			trace.WithInsecure(),
+		),
+	)
+	if err != nil {
+		fmt.Printf("init dubbo instance failed: %v\n", err)
+		os.Exit(1)
+	}
+
+	code := m.Run()
+
+	// Give exporter a short window to flush spans (best-effort).
+	time.Sleep(exporterGrace)
+	os.Exit(code)
+}
+
+func waitPort(addr string, deadline time.Duration) error {
+	end := time.Now().Add(deadline)
+	for {
+		c, err := net.DialTimeout("tcp", addr, 1*time.Second)
+		if err == nil {
+			c.Close()
+			return nil
+		}
+		if time.Now().After(end) {
+			return fmt.Errorf("port %s not ready: %w", addr, err)
+		}
+		time.Sleep(200 * time.Millisecond)
+	}
+}
+
+func TestMultiProtocols(t *testing.T) {
+
+	must := func(err error) {
+		if err != nil {
+			panic(err)
+		}
+	}
+	must(waitPort("127.0.0.1:20000", 10*time.Second)) // triple
+	must(waitPort("127.0.0.1:20001", 10*time.Second)) // dubbo(getty)
+	must(waitPort("127.0.0.1:20002", 10*time.Second)) // jsonrpc
+
+	t.Run("triple", testTriple)
+	t.Run("dubbo", testDubbo)
+	t.Run("jsonrpc", testJSONRPC)
+}
+
+// ----- Subtests -----
+
+func testTriple(t *testing.T) {
+	t.Helper()
+
+	cli, err := ins.NewClient(
+		client.WithClientProtocolTriple(),
+		client.WithClientURL(tripleAddr),
+	)
+	if err != nil {
+		t.Fatalf("create triple client: %v", err)
+	}
+
+	svc, err := greetpb.NewGreetService(cli)
+	if err != nil {
+		t.Fatalf("create greet service: %v", err)
+	}
+
+	ctx, cancel := context.WithTimeout(context.Background(), testTimeout)
+	defer cancel()
+
+	resp, err := svc.Greet(ctx, &greetpb.GreetRequest{Name: "hello world"})
+	if err != nil {
+		t.Fatalf("triple call failed: %v", err)
+	}
+
+	if resp == nil || resp.Greeting == "" {
+		t.Fatalf("unexpected triple response: %#v", resp)
+	}
+	logger.Infof("[triple] Greet response: %s", resp.Greeting)
+
+	time.Sleep(exporterGrace) // allow spans to export
+}
+
+func testDubbo(t *testing.T) {
+	t.Helper()
+
+	cli, err := ins.NewClient(
+		client.WithClientProtocolDubbo(),
+		client.WithClientSerialization(constant.Hessian2Serialization),
+		client.WithClientURL(dubboAddr),
+	)
+	if err != nil {
+		t.Fatalf("create dubbo client: %v", err)
+	}
+
+	conn, err := cli.Dial("GreetProvider")
+	if err != nil {
+		t.Fatalf("dial dubbo service: %v", err)
+	}
+
+	ctx, cancel := context.WithTimeout(context.Background(), testTimeout)
+	defer cancel()
+
+	var resp string
+	if err = conn.CallUnary(ctx, []interface{}{"hello", "new", "dubbo"}, &resp, "SayHello"); err != nil {
+		t.Fatalf("dubbo call failed: %v", err)
+	}
+	if resp == "" {
+		t.Fatalf("empty dubbo response")
+	}
+	logger.Infof("[dubbo] SayHello response: %s", resp)
+
+	time.Sleep(exporterGrace)
+}
+
+func testJSONRPC(t *testing.T) {
+	t.Helper()
+
+	cli, err := ins.NewClient(
+		client.WithClientProtocolJsonRPC(),
+		client.WithClientSerialization(constant.JSONSerialization),
+		client.WithClientURL(jsonRPCAddr),
+	)
+	if err != nil {
+		t.Fatalf("create jsonrpc client: %v", err)
+	}
+
+	conn, err := cli.Dial("GreetProvider")
+	if err != nil {
+		t.Fatalf("dial jsonrpc service: %v", err)
+	}
+
+	ctx, cancel := context.WithTimeout(context.Background(), testTimeout)
+	defer cancel()
+
+	var resp string
+	if err = conn.CallUnary(ctx, []interface{}{"hello", "new", "jsonrpc"}, &resp, "SayHello"); err != nil {
+		t.Fatalf("jsonrpc call failed: %v", err)
+	}
+	if resp == "" {
+		t.Fatalf("empty jsonrpc response")
+	}
+	logger.Infof("[jsonrpc] SayHello response: %s", resp)
+
+	time.Sleep(exporterGrace)
+}
diff --git a/otel/tracing/otlp_http_exporter/README.md b/otel/tracing/otlp_http_exporter/README.md
new file mode 100644
index 00000000..dbb15918
--- /dev/null
+++ b/otel/tracing/otlp_http_exporter/README.md
@@ -0,0 +1,30 @@
+# otlp http tracing exporter
+
+English | [中文](README_CN.md)
+
+The integration checks dubbo/triple/jsonrpc tracing feature, and uses OpenTelemetry's otlp http exporter to export the tracing data.
+
+The test exports the tracing data to a local mock http server.
+
+## How to run
+
+### Run server
+
+```shell
+go run ./go-server/cmd/main.go
+```
+
+### Run test (client)
+
+```shell
+go test -tags integration -v ./tests/integration/... 
+```
+
+If test success, you will see the log like below in the server terminal:
+
+```shell
+2025-09-18 16:22:29	INFO	cmd/main.go:127	server count: 3, client count: 3
+```
+
+If the test fails, you will see the panic in the server terminal.
+
diff --git a/otel/tracing/otlp_http_exporter/README_CN.md b/otel/tracing/otlp_http_exporter/README_CN.md
new file mode 100644
index 00000000..59819fcc
--- /dev/null
+++ b/otel/tracing/otlp_http_exporter/README_CN.md
@@ -0,0 +1,29 @@
+# otlp http tracing exporter
+
+[English](README.md) | 中文
+
+该集成测试检查了 dubbo/triple/jsonrpc 的链路追踪功能,并使用 OpenTelemetry 的 otlp http exporter 来导出追踪数据。
+
+测试会将追踪数据导出到本地的 mock http 服务器。
+
+## 如何运行
+
+### 启动服务端
+
+```shell
+go run ./go-server/cmd/main.go
+```
+
+### 运行测试(客户端)
+
+```shell
+go test -tags integration -v ./tests/integration/... 
+```
+
+如果测试成功,你会在服务端终端看到类似如下的日志:
+
+```shell
+2025-09-18 16:22:29	INFO	cmd/main.go:127	server count: 3, client count: 3
+```
+
+如果测试失败,你会在服务端终端看到 panic。
diff --git a/otel/tracing/otlp_http_exporter/go-server/cmd/main.go b/otel/tracing/otlp_http_exporter/go-server/cmd/main.go
new file mode 100644
index 00000000..e7a69fbc
--- /dev/null
+++ b/otel/tracing/otlp_http_exporter/go-server/cmd/main.go
@@ -0,0 +1,245 @@
+/*
+ * 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 (
+	"compress/gzip"
+	"context"
+	"errors"
+	"fmt"
+	"io"
+	"net"
+	"net/http"
+	"strings"
+	"time"
+)
+
+import (
+	"dubbo.apache.org/dubbo-go/v3"
+	"dubbo.apache.org/dubbo-go/v3/common"
+	_ "dubbo.apache.org/dubbo-go/v3/imports"
+	"dubbo.apache.org/dubbo-go/v3/otel/trace"
+	"dubbo.apache.org/dubbo-go/v3/protocol"
+	"dubbo.apache.org/dubbo-go/v3/server"
+
+	"github.com/dubbogo/gost/log/logger"
+
+	"github.com/golang/protobuf/proto"
+
+	collecttracepb "go.opentelemetry.io/proto/otlp/collector/trace/v1"
+)
+
+import (
+	greet "github.com/apache/dubbo-go-samples/otel/tracing/stdout/proto"
+)
+
+type GreetMultiRPCServer struct {
+}
+
+func (srv *GreetMultiRPCServer) Greet(ctx context.Context, req *greet.GreetRequest) (*greet.GreetResponse, error) {
+	resp := &greet.GreetResponse{Greeting: req.Name}
+	return resp, nil
+}
+
+type GreetProvider struct {
+}
+
+func (*GreetProvider) SayHello(req string, req1 string, req2 string) (string, error) {
+	return req + " " + req1 + " " + req2, nil
+}
+
+var (
+	// triple + dubbo + jsonrpc
+	serverReceivesChan = make(chan bool, 3)
+	clientReceivesChan = make(chan bool, 3)
+	errChan            = make(chan error, 6)
+)
+
+func mockOtlpReceiver() {
+	mux := http.NewServeMux()
+	mux.HandleFunc("/v1/traces", func(w http.ResponseWriter, r *http.Request) {
+		if r.Method != http.MethodPost {
+			http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
+			return
+		}
+		ct := strings.ToLower(r.Header.Get("Content-Type"))
+		if ct != "" && !strings.HasPrefix(ct, "application/x-protobuf") {
+			http.Error(w, "unsupported content type", http.StatusUnsupportedMediaType)
+			return
+		}
+
+		body := http.MaxBytesReader(w, r.Body, 10<<20) // 10MiB
+		defer body.Close()
+
+		var reader io.Reader = body
+		if strings.EqualFold(r.Header.Get("Content-Encoding"), "gzip") {
+			gr, err := gzip.NewReader(body)
+			if err != nil {
+				select {
+				case errChan <- err:
+				default:
+				}
+				http.Error(w, err.Error(), http.StatusBadRequest)
+				return
+			}
+			defer gr.Close()
+			reader = gr
+		}
+
+		raw, err := io.ReadAll(reader)
+		if err != nil {
+			select {
+			case errChan <- err:
+			default:
+			}
+			http.Error(w, err.Error(), http.StatusBadRequest)
+			return
+		}
+
+		var req collecttracepb.ExportTraceServiceRequest
+		if err := proto.Unmarshal(raw, &req); err != nil {
+			select {
+			case errChan <- err:
+			default:
+			}
+			http.Error(w, err.Error(), http.StatusBadRequest)
+			return
+		}
+
+		reqStr := req.String()
+		switch {
+		case strings.Contains(reqStr, "dubbo_otel_server"):
+			serverReceivesChan <- true
+		case strings.Contains(reqStr, "dubbo_otel_client"):
+			clientReceivesChan <- true
+		default:
+			select {
+			case errChan <- errors.New("unknown trace: " + reqStr):
+			default:
+			}
+		}
+
+		respBytes, _ := proto.Marshal(&collecttracepb.ExportTraceServiceResponse{})
+		w.Header().Set("Content-Type", "application/x-protobuf")
+		w.WriteHeader(http.StatusOK)
+		_, _ = w.Write(respBytes)
+	})
+
+	ln, err := net.Listen("tcp", "127.0.0.1:4318")
+	if err != nil {
+		panic(fmt.Errorf("mock OTLP receiver listen failed: %w", err))
+	}
+
+	srv := &http.Server{
+		Handler:           mux,
+		ReadHeaderTimeout: 5 * time.Second,
+	}
+
+	go func() {
+		logger.Infof("[mock-otlp] listening on %s", ln.Addr().String())
+		if err := srv.Serve(ln); err != nil && !errors.Is(err, http.ErrServerClosed) {
+			select {
+			case errChan <- err:
+			default:
+			}
+		}
+	}()
+
+	deadline := time.Now().Add(5 * time.Second)
+	for {
+		conn, dErr := net.DialTimeout("tcp", "127.0.0.1:4318", 500*time.Millisecond)
+		if dErr == nil {
+			_ = conn.Close()
+			break
+		}
+		if time.Now().After(deadline) {
+			panic(fmt.Errorf("mock OTLP not ready in time: %w", dErr))
+		}
+		time.Sleep(100 * time.Millisecond)
+	}
+}
+
+func main() {
+	mockOtlpReceiver()
+	go func() {
+		var (
+			serverCount = 0
+			clientCount = 0
+		)
+		for i := 0; i < 6; i++ {
+			select {
+			case <-serverReceivesChan:
+				serverCount++
+			case <-clientReceivesChan:
+				clientCount++
+			case err := <-errChan:
+				panic(err)
+			case <-time.After(20 * time.Second):
+				panic("timeout")
+			}
+		}
+
+		logger.Infof("server count: %d, client count: %d", serverCount, clientCount)
+		if serverCount != 3 || clientCount != 3 {
+			panic("trace received count not match")
+		}
+	}()
+
+	ins, err := dubbo.NewInstance(
+		dubbo.WithName("dubbo_otel_server"),
+		dubbo.WithTracing(
+			trace.WithEnabled(),
+			trace.WithOtlpHttpExporter(),
+			trace.WithW3cPropagator(),
+			trace.WithAlwaysMode(),
+			trace.WithEndpoint("127.0.0.1:4318"),
+			trace.WithInsecure(),
+		),
+		dubbo.WithProtocol(
+			protocol.WithTriple(),
+			protocol.WithPort(20000)),
+		dubbo.WithProtocol(
+			protocol.WithDubbo(),
+			protocol.WithPort(20001)),
+		dubbo.WithProtocol(
+			protocol.WithJSONRPC(),
+			protocol.WithPort(20002)),
+	)
+	if err != nil {
+		panic(err)
+	}
+
+	srv, err := ins.NewServer()
+	if err != nil {
+		panic(err)
+	}
+
+	//Triple
+	if err = greet.RegisterGreetServiceHandler(srv, &GreetMultiRPCServer{}); err != nil {
+		panic(err)
+	}
+
+	//Dubbo & JsonRPC
+	if err = srv.Register(&GreetProvider{}, &common.ServiceInfo{}, server.WithInterface("GreetProvider")); err != nil {
+		panic(err)
+	}
+
+	if err = srv.Serve(); err != nil {
+		logger.Error(err)
+	}
+}
diff --git a/start_integrate_test.sh b/start_integrate_test.sh
index bf2a8d28..821bcf89 100755
--- a/start_integrate_test.sh
+++ b/start_integrate_test.sh
@@ -37,6 +37,7 @@ array+=("compatibility/metrics")
 
 # tracing
 array+=("otel/tracing/stdout")
+array+=("otel/tracing/otlp_http_exporter")
 
 # direct
 array+=("compatibility/direct")