Skip to content

Conversation

@Tsukikage7
Copy link
Contributor

@Tsukikage7 Tsukikage7 commented Jan 6, 2026

What this PR does

This PR implements Triple protocol generic call support for dubbo-go, enabling Go clients to invoke Java Dubbo services without pre-generated stubs and vice versa.

Related PR

apache/dubbo-go-samples#1016 provides usage examples for Triple protocol generic call.

Background

Generic call (泛化调用) is a core feature in Dubbo ecosystem that allows service invocation without IDL/interface definitions. While dubbo-go already supports generic call for the Dubbo protocol, Triple protocol generic call was missing, making it impossible for Go and Java services to interoperate via Triple protocol in non-IDL mode.

Design Overview

The key challenge is that Java Dubbo Triple protocol uses a specific wire format for non-IDL mode:

┌─────────────────────────────────────────────────────────────┐
│                    Wire Format                              │
├─────────────────────────────────────────────────────────────┤
│  Content-Type: application/proto                            │
│                                                             │
│  Request:  TripleRequestWrapper (protobuf)                  │
│            ├── serializeType: "hessian2"                    │
│            ├── args: [hessian2(arg1), hessian2(arg2), ...]  │
│            └── argTypes: ["java.lang.String", ...]          │
│                                                             │
│  Response: TripleResponseWrapper (protobuf)                 │
│            ├── serializeType: "hessian2"                    │
│            ├── data: hessian2(result)                       │
│            └── type: "org.apache.dubbo.samples.User"        │
└─────────────────────────────────────────────────────────────┘

This is a two-layer serialization: protobuf wrapper on the outside, hessian2 data on the inside.

Implementation Details

1. New WrapperCodec Interface (codec.go)

type WrapperCodec interface {
    Codec
    WireCodecName() string  // Returns "proto" for Content-Type
}

This interface allows a codec to use different names for:

  • Internal registration: Name() returns "hessian2"
  • Wire format: WireCodecName() returns "proto"

2. New protoWrapperCodec (codec.go)

Client-side codec that:

  • Wraps request arguments in TripleRequestWrapper
  • Unwraps response from TripleResponseWrapper
  • Uses hessian2 for inner data serialization
type protoWrapperCodec struct {
    innerCodec Codec  // hessian2Codec
}

func (c *protoWrapperCodec) Marshal(message any) ([]byte, error) {
    // 1. Serialize each arg with hessian2
    // 2. Get Java type for each arg
    // 3. Wrap in TripleRequestWrapper
    // 4. Marshal with protobuf
}

3. Enhanced protoBinaryCodec (codec.go)

Server-side enhancement to handle incoming wrapped requests:

  • Detects non-proto.Message types
  • Unwraps from TripleRequestWrapper/TripleResponseWrapper
  • Deserializes with hessian2

4. Generic Filter Enhancement (filter/generic/filter.go)

Added parameterRawValues support for Triple protocol in both branches:

  • isCallingToGenericService: When user calls normal method (e.g., GetUser), filter transforms to $invoke format
  • isMakingAGenericCall: When user directly calls $invoke, filter sets up parameterRawValues for Triple
// For Triple protocol, we need [methodName, types, args, reply]
// Triple invoker slices as: request = inRaw[0:len-1], reply = inRaw[len-1]
parameterRawValues := []any{mtdName, types, args, reply}

5. Server-side Generic Call Handler (protocol/triple/server.go)

Auto-registers $invoke method for Triple protocol services via buildGenericMethodInfo():

func buildGenericMethodInfo() common.MethodInfo {
    return common.MethodInfo{
        Name: constant.Generic,  // "$invoke"
        Type: constant.CallUnary,
        ReqInitFunc: func() any {
            return []any{
                func(s string) *string { return &s }(""),
                &[]string{},
                &[]hessian.Object{},
            }
        },
    }
}

Call Flow

Request (Go Client → Java Server)

Step Component Action Output
1 Client genericService.Invoke(ctx, "GetUser", types, args) Method call
2 GenericService Transform to $invoke $invoke("GetUser", ["java.lang.String"], ["user123"])
3 protoWrapperCodec Serialize with hessian2 []byte for each arg
4 protoWrapperCodec Wrap in TripleRequestWrapper protobuf{serializeType, args[], argTypes[]}
5 Network Send via HTTP/2 Content-Type: application/proto

Response (Java Server → Go Client)

Step Component Action Output
6 Server Execute method, wrap response TripleResponseWrapper{data, type}
7 Network Return via HTTP/2 Content-Type: application/proto
8 protoWrapperCodec Unwrap TripleResponseWrapper hessian2 bytes
9 protoWrapperCodec Deserialize with hessian2 User object
10 Client Return result map[string]interface{}

Files Changed

File Changes
protocol/triple/triple_protocol/codec.go Add WrapperCodec interface, protoWrapperCodec, enhance protoBinaryCodec
protocol/triple/triple_protocol/codec_wrapper_test.go Comprehensive unit tests (631 lines)
filter/generic/filter.go Add parameterRawValues and CallType for Triple protocol in both branches
client/client.go Add generic service client support
client/options.go Add WithGeneric() option
protocol/triple/client.go Wire up wrapper codec for generic mode
protocol/triple/server.go Add $invoke method registration, handle generic call on server side
protocol/triple/triple.go Minor adjustments for generic support
common/constant/key.go Add generic-related constants
protocol/triple/triple_protocol/protocol_grpc.go Use getWireCodecName() for Content-Type
protocol/triple/triple_protocol/protocol_triple.go Use getWireCodecName() for Content-Type

Testing

Unit Tests: 631 lines of comprehensive tests covering:

  • protoWrapperCodec marshal/unmarshal
  • protoBinaryCodec wrapper handling
  • Various data types (string, int, POJO, arrays, maps)
  • Edge cases and error scenarios

Integration Tests:

Scenario Result
Go client → Java server (Dubbo protocol) PASS
Go client → Java server (Triple protocol) PASS
Java client → Go server (Triple protocol) PASS
Go client → Go server (Triple protocol) PASS

Usage Example

// Create client with Triple protocol
cli, _ := ins.NewClient(
    client.WithClientProtocolTriple(),
    client.WithClientSerialization(constant.Hessian2Serialization),
)

// Create generic service client
genericService, _ := cli.NewGenericService(
    "org.apache.dubbo.samples.UserProvider",
    client.WithURL("tri://127.0.0.1:50052"),
    client.WithVersion("1.0.0"),
    client.WithGroup("triple"),
)

// Make generic call
result, err := genericService.Invoke(
    ctx,
    "GetUser",                           // Method name
    []string{"java.lang.String"},        // Parameter types
    []hessian.Object{"user123"},         // Arguments
)
// result is map[string]interface{} for POJO returns

Compatibility

  • Backward Compatible: Existing IDL-based Triple calls are not affected
  • Cross-Language: Fully compatible with Java Dubbo 3.x Triple protocol
  • Dubbo Protocol: Existing Dubbo protocol generic call still works as before

Signed-off-by: TsukiKage chongyanx@163.com

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR implements Triple protocol generic call support for dubbo-go, enabling Go clients to invoke Java Dubbo services without pre-generated stubs and vice versa. The implementation uses a two-layer serialization approach: protobuf wrapper on the outside (TripleRequestWrapper/TripleResponseWrapper) with hessian2-encoded data on the inside, matching Java Dubbo's wire format.

Key changes:

  • Introduced WrapperCodec interface and protoWrapperCodec implementation for handling the two-layer serialization format
  • Enhanced protoBinaryCodec to automatically detect and unwrap generic calls on the server side
  • Added reflection-based method invocation infrastructure to support dynamic service method calls

Reviewed changes

Copilot reviewed 11 out of 11 changed files in this pull request and generated 9 comments.

Show a summary per file
File Description
protocol/triple/triple_protocol/codec.go Adds WrapperCodec interface, protoWrapperCodec for client-side wrapping, and enhances protoBinaryCodec for server-side unwrapping
protocol/triple/triple_protocol/codec_wrapper_test.go Comprehensive unit tests (631 lines) covering wrapper codec functionality, type mapping, and edge cases
server/server.go Adds reflection-based method invocation support and enhances service info with $invoke method registration
protocol/triple/server.go Refactors generic method info building and adds reflection-based MethodFunc creation
protocol/triple/client.go Routes generic calls to $invoke method and registers it for non-IDL mode
filter/generic/filter.go Adds parameterRawValues support for Triple protocol generic calls
protocol/triple/triple.go Routes to NewTripleInvoker when generic call is detected
protocol/triple/triple_protocol/protocol_grpc.go Uses getWireCodecName for correct Content-Type header
protocol/triple/triple_protocol/protocol_triple.go Uses getWireCodecName for correct Content-Type header
common/constant/key.go Adds protobuf and protobuf-json generic serialization constants
client/options.go Adds WithGenericType option for specifying generic serialization type

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

@Tsukikage7 Tsukikage7 force-pushed the feat/generic-call branch 4 times, most recently from 64d3d19 to 994396b Compare January 6, 2026 06:04
Add generic call support for Triple protocol in non-IDL mode, enabling
Go clients to invoke Java services without pre-generated stubs.

Changes:
- Add protoWrapperCodec for wrapping hessian2 data in protobuf format
- Support protoBinaryCodec unmarshaling wrapped hessian2 responses
- Set CallType and parameterRawValues in generic filter for Triple
- Add NewGenericService API in client package
@sonarqubecloud
Copy link

sonarqubecloud bot commented Jan 6, 2026

@codecov-commenter
Copy link

Codecov Report

❌ Patch coverage is 41.40969% with 133 lines in your changes missing coverage. Please review.
✅ Project coverage is 48.00%. Comparing base (489ee6a) to head (b50ad31).

Files with missing lines Patch % Lines
server/server.go 6.12% 41 Missing and 5 partials ⚠️
protocol/triple/server.go 46.29% 28 Missing and 1 partial ⚠️
protocol/triple/triple_protocol/codec.go 58.92% 18 Missing and 5 partials ⚠️
client/client.go 0.00% 11 Missing ⚠️
protocol/triple/triple.go 27.27% 8 Missing ⚠️
protocol/triple/client.go 41.66% 5 Missing and 2 partials ⚠️
filter/generic/filter.go 79.31% 2 Missing and 4 partials ⚠️
client/options.go 0.00% 3 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #3154      +/-   ##
==========================================
+ Coverage   47.80%   48.00%   +0.19%     
==========================================
  Files         460      460              
  Lines       33011    33187     +176     
==========================================
+ Hits        15782    15930     +148     
- Misses      15963    15967       +4     
- Partials     1266     1290      +24     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@Tsukikage7 Tsukikage7 changed the base branch from main to develop January 6, 2026 07:55
@AlexStocks AlexStocks added the 3.3.2 version 3.3.2 label Jan 8, 2026
Copy link
Member

@No-SilverBullet No-SilverBullet left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

3.3.2 version 3.3.2

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants