background

HTTP interface testing only requires a curl command, but the Dubbo protocol has no such off-the-shelf interface testing tool. Often a dubbo interface testing tool is integrated into the company’s Dubbo console or other platform.

To invoke a Dubbo interface, you need to know the name of service, the name of method, and the parameter args.

For a normal invocation, the caller needs to introduce the interface JAR package defined by the service provider.

As an interface test platform, it is not possible to introduce all the interface JAR packages defined by the provider. The following solutions can be used to solve this problem:

  1. Dubbo Allows Telnet to invoke the Dubbo interface
  2. A generic call to Dubbo can be made to the interface without introducing the provider interface definition JAR package

For Scenario 1, implementation costs are low and you can even test directly on the server using Telnet

It also has disadvantages

  • The call cannot pass through the filter
  • Cannot carry the implicit parameter attachment

We just stepped on the advantages and disadvantages of scheme 1, our Dubbo console is written in go language, short time fast implementation, we adopted the Telnet way.

With the development of services, implicit parameters such as traffic coloring and label routing need to be carried.

We were forced to upgrade to a generic call because the business interface did not perform as expected without a custom filter.

The dubbo interface generalization call also has two options when the console is written by GO:

  1. Set up a separate Java process, expose HTTP ports, interact with the GO process, and generalize calls written using Dubbo’s Java SDK
  2. The console introduces Dubbo-Go and uses dubbo-go for the generalization call

Knowing the Java version of Dubbo, option 1 certainly works, but the architecture gets complicated.

As for solution 2, dubbo-Go is still a relatively new project and I don’t know much about it, so I’m not sure about its feasibility and compatibility, but if it can be implemented, it will greatly reduce the complexity of the architecture.

Dubbo – go

Dubbo-go is Dubbo’s Golang implementation, originally designed to allow Golang to interact with Java’s Dubbo ecosystem.

Dubbo-go now supports both provider and consumer sides and can be used as a stand-alone RPC framework, and the community is one of the most popular in the Dubbo ecosystem.

If anything, I think it’s important that, in addition to interworking with Java, it makes use of the Golang coroutine. This can be applied to the Dubbo gateway. If you implement the Dubbo gateway with Dubbo-go, you don’t have to worry about thread pools, async, etc.

The use of generalization calls

First of all, the provider side provides an interface, which is very simple. The definition of the interface is as follows

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);
}
Copy the code
package org.newboo.basic.model;

import java.io.Serializable;

public class User implements Serializable {
    private String uid;
    private String name;
    privateString remoteServiceTag; . }Copy the code

Let’s write the Java version of the generic call code without introducing the Provider jar package:

ReferenceConfig<GenericService> reference = new ReferenceConfig<>();
// ① Reference the service name
reference.setInterface("org.newboo.basic.api.MyDemoService");
// ② Set the generalization call 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];
// ③ Parameter types
ps[0] = "org.newboo.basic.model.User";
Object[] ags = new Object[1];
// ④ PoJO parameters are constructed using map
Map<String, String> user = new HashMap<>();
user.put("uid"."1");
user.put("name"."roshi");
user.put("remoteServiceTag"."tag");
ags[0] = user;
// make the call
Object res = genericService.$invoke("call", ps, ags);
System.out.println(res);
Copy the code

The key steps are identified in the code comments

Golang version

For the directly modified Dubbo-go-samples code, see github.com/apache/dubb… The configuration file path ENV needs to be set during startup

var (
	appName         = "UserConsumer"
	referenceConfig = config.ReferenceConfig{
		InterfaceName: "org.newboo.basic.api.MyDemoService",
		Cluster:       "failover".// Registry requires configuration files
		Registry:      "demoZk",
		Protocol:      dubbo.DUBBO,
		Generic:       true,})func init(a) {
	referenceConfig.GenericLoad(appName) //appName is the unique identification of RPCService
	time.Sleep(1 * time.Second)
}

// need to setup environment variable "CONF_CONSUMER_FILE_PATH" to "conf/client.yml" before run
func main(a) {
	call()
}

func call(a) {
  / / set the attachment
	ctx := context.WithValue(context.TODO(), constant.AttachmentKey, map[string]string{"tag":"test"})

	resp, err := referenceConfig.GetRPCService().(*config.GenericService).Invoke(
		ctx,
		[]interface{} {"call"And []string{"org.newboo.basic.model.User"},
			[]interface{} {map[string]string{"uid":"111"."name":"roshi"."remoteServiceTag":"hello"}}},)iferr ! =nil {
		panic(err)
	}
	gxlog.CInfo("success called res: %+v\n", resp)
}
Copy the code

Here I set an attachment, which can also be recognized by the provider normally

Generalize the invocation principle

GenericService is a service provided by dubbo by default.

It provides a method named $invoke that takes three arguments. The first argument is the name of the actual method to be called, the second is an array of parameter types, and the third is the actual parameter array, defined as

public interface GenericService {
    Object $invoke(String method, String[] parameterTypes, Object[] args) throwsGenericException; . }Copy the code

With these three parameters, you can invoke the real interface using reflection.

Java version implementation details

Implementing this generalization call involves two filters:

  • The consumer end GenericImplFilter
  • The provider side GenericFilter

The Consumer filter sets the Generic flag to the attachment and encapsulates the GenericService.$invoke

The Provider filter intercepts the generic request, obtains the method name, parameters, and parameter values, serializes the request to a POJO object, and then invokes the real interface through reflection.

Dubbo – go version details

Generic_filter: GenericService.$invoke: GenericService.$invoke: GenericService. Convert the map to Dubbo-go-Hessian2. Object so that the provider can deserialize it to an Object.

Related version changes are as follows

  • V1.3.0 began to support generic calls
  • V1.4.0 starts to support attachement setting
  • V. 15.1 Starting to Support dynamic TAG Routing
  • V1.5.7-rc1 fixed the bug that the filter cannot be accessed when the provider is directly connected

Step down: Before V1.5.7-RC1, if the direct provider is used, the filter is not displayed. As a result, parameter serialization fails, and the Provider reports a type conversion exception

conclusion

The generic call to Dubbo-go is recommended to use the >= V1.5.7-RC1 version, which is almost on par with the Java version and even has a Java-like implementation.

Using Dubbo-Go to build a gateway, an interface test platform, or to connect Golang to the Java technology ecosystem is a good choice.


Search and follow the wechat public account “catch bug master”, back-end technology sharing, architecture design, performance optimization, source code reading, problem troubleshooting, stepping pit practice.