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-pixiu-samples.git
The following commit(s) were added to refs/heads/main by this push:
new 099525c feat(grpc): add gRPC Server Reflection demo for issue #821
(#120)
099525c is described below
commit 099525caccb0c706475e927dea029899885819dc
Author: Tsukikage <[email protected]>
AuthorDate: Fri Jan 9 19:37:39 2026 +0800
feat(grpc): add gRPC Server Reflection demo for issue #821 (#120)
* feat(grpc): add gRPC Server Reflection demo for issue #821
Add a complete demo showcasing gRPC Server Reflection support with:
- EchoService with all 4 RPC types (unary, server/client/bidirectional
streaming)
- Server with reflection.Register() enabled
- Three Pixiu config examples (passthrough, reflection, hybrid modes)
- Client and integration tests
- Bilingual documentation (EN/CN)
Ref: https://github.com/apache/dubbo-go-pixiu/issues/821
Ref: https://github.com/apache/dubbo-go-pixiu/pull/849
* fix(grpc): replace deprecated grpc.Dial with grpc.NewClient
grpc.Dial is deprecated since gRPC-Go 1.x, use NewClient instead
* docs: update README and add gRPC reflection demo to integration test
- Add gRPC reflection demo description to README.md and README_CN.md
- Add grpc/reflection to start_integrate_test.sh for CI integration
- Unify pixiu config ports to 8881 for reflection and hybrid modes
- Update go.mod to use pkg/errors as direct dependency
* fix(grpc/reflection): move server.go to server/app/ for Makefile
compatibility
The integration test Makefile expects Go files in server/app/ directory.
Move server.go to match the expected structure.
---
grpc/reflection/server/app/server.go | 182 +++++++++++++++++++++++++++++++++++
1 file changed, 182 insertions(+)
diff --git a/grpc/reflection/server/app/server.go
b/grpc/reflection/server/app/server.go
new file mode 100644
index 0000000..f1a09ad
--- /dev/null
+++ b/grpc/reflection/server/app/server.go
@@ -0,0 +1,182 @@
+/*
+ * 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 implements a gRPC server with Server Reflection enabled.
+// This demonstrates the gRPC Server Reflection feature for dynamic message
parsing.
+package main
+
+import (
+ "context"
+ "flag"
+ "fmt"
+ "io"
+ "log"
+ "net"
+ "os"
+ "time"
+)
+
+import (
+ "google.golang.org/grpc"
+ "google.golang.org/grpc/reflection"
+)
+
+import (
+ pb "github.com/dubbo-go-pixiu/samples/grpc/reflection/proto"
+)
+
+var (
+ port = flag.Int("port", 50051, "The server port")
+ serverID = flag.String("server_id", "", "Server identifier for load
balancing verification")
+)
+
+// echoServer implements the EchoService.
+type echoServer struct {
+ pb.UnimplementedEchoServiceServer
+ serverID string
+}
+
+// Echo returns the message back with additional metadata.
+func (s *echoServer) Echo(ctx context.Context, req *pb.EchoRequest)
(*pb.EchoResponse, error) {
+ log.Printf("[Echo] Received message: %s", req.Message)
+
+ return &pb.EchoResponse{
+ Message: req.Message,
+ ServerTimestamp: time.Now().UnixNano(),
+ ReflectionEnabled: true, // This server has reflection enabled
+ ServerId: s.serverID,
+ Metadata: req.Metadata,
+ }, nil
+}
+
+// StreamEcho demonstrates server streaming with reflection support.
+func (s *echoServer) StreamEcho(req *pb.EchoRequest, stream
pb.EchoService_StreamEchoServer) error {
+ log.Printf("[StreamEcho] Received message: %s", req.Message)
+
+ // Send 5 responses for demonstration
+ for i := 0; i < 5; i++ {
+ resp := &pb.EchoResponse{
+ Message: fmt.Sprintf("[%d] %s", i+1,
req.Message),
+ ServerTimestamp: time.Now().UnixNano(),
+ ReflectionEnabled: true,
+ ServerId: s.serverID,
+ Metadata: req.Metadata,
+ }
+ if err := stream.Send(resp); err != nil {
+ return err
+ }
+ time.Sleep(100 * time.Millisecond)
+ }
+ return nil
+}
+
+// ClientStreamEcho demonstrates client streaming with reflection support.
+func (s *echoServer) ClientStreamEcho(stream
pb.EchoService_ClientStreamEchoServer) error {
+ log.Printf("[ClientStreamEcho] Stream started")
+
+ var messages []string
+ var lastMetadata map[string]string
+
+ for {
+ req, err := stream.Recv()
+ if err == io.EOF {
+ // Client finished sending, return aggregated response
+ return stream.SendAndClose(&pb.EchoResponse{
+ Message: fmt.Sprintf("Received %d
messages: %v", len(messages), messages),
+ ServerTimestamp: time.Now().UnixNano(),
+ ReflectionEnabled: true,
+ ServerId: s.serverID,
+ Metadata: lastMetadata,
+ })
+ }
+ if err != nil {
+ return err
+ }
+
+ log.Printf("[ClientStreamEcho] Received: %s", req.Message)
+ messages = append(messages, req.Message)
+ lastMetadata = req.Metadata
+ }
+}
+
+// BidirectionalEcho demonstrates bidirectional streaming.
+func (s *echoServer) BidirectionalEcho(stream
pb.EchoService_BidirectionalEchoServer) error {
+ log.Printf("[BidirectionalEcho] Stream started")
+
+ for {
+ req, err := stream.Recv()
+ if err == io.EOF {
+ return nil
+ }
+ if err != nil {
+ return err
+ }
+
+ log.Printf("[BidirectionalEcho] Received: %s", req.Message)
+
+ resp := &pb.EchoResponse{
+ Message: req.Message,
+ ServerTimestamp: time.Now().UnixNano(),
+ ReflectionEnabled: true,
+ ServerId: s.serverID,
+ Metadata: req.Metadata,
+ }
+ if err := stream.Send(resp); err != nil {
+ return err
+ }
+ }
+}
+
+func getServerID() string {
+ if *serverID != "" {
+ return *serverID
+ }
+ // Use hostname as default server ID
+ hostname, err := os.Hostname()
+ if err != nil {
+ return "unknown"
+ }
+ return hostname
+}
+
+func main() {
+ flag.Parse()
+
+ lis, err := net.Listen("tcp", fmt.Sprintf(":%d", *port))
+ if err != nil {
+ log.Fatalf("failed to listen: %v", err)
+ }
+
+ // Create gRPC server with default options
+ grpcServer := grpc.NewServer()
+
+ // Register the EchoService
+ pb.RegisterEchoServiceServer(grpcServer, &echoServer{
+ serverID: getServerID(),
+ })
+
+ // IMPORTANT: Enable gRPC Server Reflection
+ // This allows Pixiu to dynamically discover and parse service methods
+ reflection.Register(grpcServer)
+
+ log.Printf("gRPC server with reflection enabled listening on port %d",
*port)
+ log.Printf("Server ID: %s", getServerID())
+
+ if err := grpcServer.Serve(lis); err != nil {
+ log.Fatalf("failed to serve: %v", err)
+ }
+}