Mastering Dubbo Generic Calls with Go: From Telnet to Dubbo-go

This article explains how to test and invoke Dubbo services without provider JARs by using telnet or generic calls, compares Java and Go implementations, details the underlying filters, and provides practical code samples for building a Dubbo-go based gateway or testing platform.

Xiao Lou's Tech Notes
Xiao Lou's Tech Notes
Xiao Lou's Tech Notes
Mastering Dubbo Generic Calls with Go: From Telnet to Dubbo-go

Background

HTTP interface testing only needs a curl command, but the Dubbo protocol lacks a ready-made testing tool. Typically, internal Dubbo consoles or platforms integrate a Dubbo testing tool.

Calling a Dubbo interface requires the service name ( service), method name ( method) and arguments ( args). Normally the caller must import the provider's interface JAR.

As a testing platform we cannot import all provider JARs, so we consider two solutions:

Dubbo supports telnet protocol for invoking Dubbo interfaces.

Dubbo generic invocation allows calling interfaces without importing provider JARs.

Solution 1 has low implementation cost and can be tested directly with telnet on the server, but it cannot pass through filters and cannot carry implicit attachment parameters.

We initially adopted telnet for quick implementation, but as business needs grew (traffic coloring, tag routing, etc.) we needed implicit parameters, which forced us to upgrade to generic invocation.

Dubbo-go Introduction

Dubbo-go is the Golang implementation of Dubbo, created to enable interoperability between Golang and Java Dubbo ecosystems. It now supports both provider and consumer sides and can be used as an independent RPC framework.

Beyond Java interoperability, Dubbo-go leverages Go's goroutine model, making it suitable for building Dubbo gateways without dealing with thread pools or asynchronous complexities.

Using Generic Invocation

The provider defines a simple interface:

package org.newboo.basic.api;

import org.newboo.basic.model.RpcResult;
import org.newboo.basic.model.User;

public interface MyDemoService {
    RpcResult<String> call(User user);
}

Java generic invocation (no provider JAR needed):

ReferenceConfig<GenericService> reference = new ReferenceConfig<>();
// ① set service name
reference.setInterface("org.newboo.basic.api.MyDemoService");
// ② enable generic flag
reference.setGeneric("true");

DubboBootstrap bootstrap = DubboBootstrap.getInstance();
bootstrap.application(new ApplicationConfig("dubbo-demo-api-consumer"))
        .registry(new RegistryConfig("zookeeper://127.0.0.1:2181"))
        .reference(reference)
        .start();

GenericService genericService = ReferenceConfigCache.getCache().get(reference);
String[] ps = new String[1];
ps[0] = "org.newboo.basic.model.User";
Object[] args = new Object[1];
Map<String, String> user = new HashMap<>();
user.put("uid", "1");
user.put("name", "roshi");
user.put("remoteServiceTag", "tag");
args[0] = user;
Object res = genericService.$invoke("call", ps, args);
System.out.println(res);

Go version (using dubbo-go):

var (
    appName = "UserConsumer"
    referenceConfig = config.ReferenceConfig{
        InterfaceName: "org.newboo.basic.api.MyDemoService",
        Cluster:       "failover",
        Registry:      "demoZk",
        Protocol:      dubbo.DUBBO,
        Generic:       true,
    }
)

func init() {
    referenceConfig.GenericLoad(appName)
    time.Sleep(1 * time.Second)
}

func main() { call() }

func call() {
    ctx := context.WithValue(context.TODO(), constant.AttachmentKey, map[string]string{"tag": "test"})
    resp, err := referenceConfig.GetRPCService().(*config.GenericService).Invoke(
        ctx,
        []interface{}{ "call" },
        []string{"org.newboo.basic.model.User"},
        []interface{}{ map[string]string{"uid":"111","name":"roshi","remoteServiceTag":"hello"} },
    )
    if err != nil { panic(err) }
    gxlog.CInfo("success called res: %+v
", resp)
}

The Go example sets an attachment that the provider can recognize.

Principle of Generic Invocation

Dubbo provides a GenericService with a method $invoke that takes three parameters: the real method name, an array of parameter type strings, and an array of actual arguments. Using reflection, the real interface is invoked.

public interface GenericService {
    Object $invoke(String method, String[] parameterTypes, Object[] args) throws GenericException;
    ...
}

Java Implementation Details

Two filters are involved:

Consumer-side GenericImplFilter sets the generic flag in the attachment and wraps the call as GenericService.$invoke.

Provider-side GenericFilter intercepts generic requests, deserializes parameters into POJOs, and invokes the real method via reflection.

Dubbo-go Implementation Details

The logic is similar. The generic_filter on the consumer side packages the call into GenericService.$invoke and converts map parameters to dubbo-go-hessian2.Object. The provider side then deserializes them back to objects.

Version milestones:

v1.3.0 – generic invocation support added.

v1.4.0 – attachment setting supported.

v1.5.1 – dynamic tag routing supported.

v1.5.7-rc1 – fixed bug where direct provider connections bypassed filters, causing serialization errors.

Conclusion

Use Dubbo-go version >= v1.5.7-rc1 for generic invocation; its features are nearly on par with the Java version, and the implementation is similar.

Dubbo-go is a solid choice for building gateways, testing platforms, or bridging Go and Java ecosystems.

JavamicroservicesRPCDubboGeneric Invocation
Xiao Lou's Tech Notes
Written by

Xiao Lou's Tech Notes

Backend technology sharing, architecture design, performance optimization, source code reading, troubleshooting, and pitfall practices

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.