×
Community Blog Simple Introduction: Building Dubbo-go from Scratch and Dubbo Usage

Simple Introduction: Building Dubbo-go from Scratch and Dubbo Usage

This article describes how to use Dubbo-go to call service providers provided by Dubbo-go or Dubbo in detail.

By Tiecheng, a member of the Dubbo-go community

1

This article describes how to use Dubbo-go to call service providers provided by Dubbo-go or Dubbo in detail.

Preface

This article is based on Dubbo-go 1.5.4.

Before the Dubbo-go development tests started recently, samples were used for functional verification. Now, to reproduce a functional problem, a project of Dubbo-go and Dubbo calling has been planned from scratch to record the potential problems newcomers might encounter.

This article contains two parts:

  • Call Dubbo and Dubbo-go service providers through the common Dubbo-go consumption configuration
  • Problem-solving strategies through a real bug

Prepare Dubbo Service Providers

Define DemoService interface:

public interface DemoService {

    String sayHello(String name);

    String sayHello(User user);

    String sayHello(User user, String name);

}

Define User object:

public class User implements Serializable {

    private String name;

    private int age;

    ......
}

Start Dubbo Service Providers

Use Dubbo official sample code:

public static void main(String[] args) throws IOException {
    // Service implementation
    DemoService demoService = new DemoServiceImpl();

    // Current application configuration
    ApplicationConfig application = new ApplicationConfig();
    application.setName("demoProvider");

    // Connect to the configuration of the registry center
    RegistryConfig registry = new RegistryConfig();
    registry.setAddress("127.0.0.1:2181");
    registry.setProtocol("zookeeper");
    registry.setUsername("");
    registry.setPassword("");

    // Protocol configuration of service providers
    ProtocolConfig protocol = new ProtocolConfig();
    protocol.setName("dubbo");
    protocol.setPort(12345);
    protocol.setThreads(200);

    // Note: ServiceConfig is a heavy instance, which internally encapsulates the connection with the registry center and starts the service port.

    // Exposed service configuration by service providers
ServiceConfig<DemoService> service = new serviceConfig<> (); // This instance is heavy, encapsulating the connection with the registry center. Please cache the instance on your own to prevent memory and connection leakage.
    service.setApplication(application);
    service.setRegistry(registry); // setRegistries() for multiple registry centers
    service.setProtocol(protocol); // setProtocols() for multiple protocols
    service.setInterface(DemoService.class);
    service.setRef(demoService);
    service.setVersion("1.0.0");
    service.setGroup("tc");
    service.setTimeout(60 * 1000);

    // Exposure and services registering
    service.export();

    System.in.read();
}

Check zookeeper to see if services have been registered successfully:

$ls /dubbo/com.funnycode.DemoService/providers
[dubbo%3A%2F%2F127.0.0.1%3A12345%2Fcom.funnycode.DemoService%3Fanyhost%3Dtrue%26application%3DdemoProvider%26deprecated%3Dfalse%26dubbo%3D2.0.2%26dynamic%3Dtrue%26generic%3Dfalse%26group%3Dtc%26interface%3Dcom.funnycode.DemoService%26methods%3DsayHello%26pid%3D18167%26release%3D2.7.7%26revision%3D1.0.0%26side%3Dprovider%26threads%3D200%26timestamp%3D1606896020691%26version%3D1.0.0]

The preceding output indicates that the service providers have been started.

  • Dubbo-go consumer preparation
  • Basic definitions

Define User object:

type User struct {
    Name string
    Age  int
}

func (User) JavaClassName() string {
    return "com.funnycode.User"
}

Define DemoProvider interface:

type DemoProvider struct {
    SayHello  func(ctx context.Context, name string) (string, error)            `dubbo:"sayHello"`
    SayHello2 func(ctx context.Context, user User) (string, error)              `dubbo:"sayHello"`
    SayHello3 func(ctx context.Context, user User, name string) (string, error) `dubbo:"sayHello"`
}

func (p *DemoProvider) Reference() string {
    return "DemoProvider"
}

Start Dubbo-go consumer

func main() {
    config.Load()
    gxlog.CInfo("\n\n\nstart to test dubbo")

    res, err := demoProvider.SayHello(context.TODO(), "tc")
    if err != nil {
        panic(err)
    }

    gxlog.CInfo("response result: %v\n", res)

    user := User{
        Name: "tc",
        Age:  18,
    }

    res, err = demoProvider.SayHello2(context.TODO(), user)
    if err != nil {
        panic(err)
    }

    gxlog.CInfo("response result: %v\n", res)

    res, err = demoProvider.SayHello3(context.TODO(), user, "tc")
    if err != nil {
        panic(err)
    }

    gxlog.CInfo("response result: %v\n", res)

    initSignal()
}

Request Result Analysis

Direct Call

Problem confirmation

The parameter of the first interface is a string, which normally returns '[2020-12-03/18:59:12 main.main: client.go: 29] response result: Hello tc'.

The second and third interfaces cannot be called because of the User object. The error messages are as follows:

2020-12-02T17:10:47.739+0800    INFO    getty/listener.go:87    session{session session-closed, Read Bytes: 924, Write Bytes: 199, Read Pkgs: 0, Write Pkgs: 1} got error{java exception:Fail to decode request due to: java.lang.IllegalArgumentException: Service not found:com.funnycode.DemoService, sayHello
        at org.apache.dubbo.rpc.protocol.dubbo.DecodeableRpcInvocation.decode(DecodeableRpcInvocation.java:134)
        at org.apache.dubbo.rpc.protocol.dubbo.DecodeableRpcInvocation.decode(DecodeableRpcInvocation.java:80)
        at org.apache.dubbo.remoting.transport.DecodeHandler.decode(DecodeHandler.java:57)
        at org.apache.dubbo.remoting.transport.DecodeHandler.received(DecodeHandler.java:44)
        at org.apache.dubbo.remoting.transport.dispatcher.ChannelEventRunnable.run(ChannelEventRunnable.java:57)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
        at java.lang.Thread.run(Thread.java:748)
}, will be closed.

Errors are as described in the issue. Since the error information is returned to the consumers, the error stack information in Java is available to check. Therefore, check DecodeableRpcInvocation.decode#134 directly.

View Breakpoint

The code is listed below:

// Deserialization
public class DecodeableRpcInvocation extends RpcInvocation implements Codec, Decodeable {
    public Object decode(Channel channel, InputStream input) throws IOException {
      ......
      if (serviceDescriptor != null) {
          // Search by method name in method description
          MethodDescriptor methodDescriptor = serviceDescriptor.getMethod(getMethodName(), desc);
          if (methodDescriptor != null) {
              pts = methodDescriptor.getParameterClasses();
              this.setReturnTypes(methodDescriptor.getReturnTypes());
          }
      }
      // Indicate that no methods are found        
      if (pts == DubboCodec.EMPTY_CLASS_ARRAY) {
          if (!RpcUtils.isGenericCall(path, getMethodName()) && !RpcUtils.isEcho(path, getMethodName())) {
              throw new IllegalArgumentException("Service not found:" + path + ", " + getMethodName());
          }
          pts = ReflectUtils.desc2classArray(desc);
      }
      ......
    }
}
  • View MethodDescriptor, to find out whether there are methods. If methods exist, ParameterClasses will be set.
  • If there are no methods, the condition pts == DubboCodec.EMPTY_CLASS_ARRAY will be met to determine whether it is a generic call or an echo call. If neither is true, it will report that the service cannot be found and the method is wrong.
  • Desc is the Ljava/lang/Object method and, no method exists in which a parameter is an Object, so an error is inevitable.

Note: Method Query

The code is listed below:

public MethodDescriptor getMethod(String methodName, String params) {
    Map<String, MethodDescriptor> methods = descToMethods.get(methodName);
    if (CollectionUtils.isNotEmptyMap(methods)) {
        return methods.get(params);
    }
    return null;
}

Advantages: Compared with the previous version, the method metadata is cached. Therefore, the efficiency is improved without reflection, namely, trading time for space.

2

Locating the Problem

Direct code writing cannot fix the problem, so a comparison is provided to locate the problem.

Start Dubbo Consumers

Start the Dubbo consumers through the API mode. For more information, please see the official example. This operation is to view the transmission content of the Java version.

public static void main(String[] args) throws InterruptedException {
    // Current application configuration
    ApplicationConfig application = new ApplicationConfig();
    application.setName("demoProvider2");

    // Connect to the configuration of the registry center
    RegistryConfig registry = new RegistryConfig();
    registry.setAddress("127.0.0.1:2181");
    registry.setProtocol("zookeeper");
    registry.setUsername("");
    registry.setPassword("");
    // Note: ReferenceConfig is a heavy instance, which internally encapsulates the connection with the registry center and the service provider.

    // Reference of remote services
    ReferenceConfig<DemoService> reference
        = new ReferenceConfig<>(); // This instance is very heavy, encapsulating the connection with the registry center and the service provider. Please cache the instance on your own to prevent memory and connection leakage.
    reference.setApplication(application);
    reference.setRegistry(registry); // setRegistries() for multiple registry centers
    reference.setInterface(DemoService.class);
    reference.setVersion("1.0.0");
    reference.setGroup("tc");
    reference.setCheck(true);
    reference.setTimeout(1000 * 60);

    // Use xxxService like a local bean
    DemoService demoService = reference.get(); // Note: The proxy instance encapsulates all communication details. The instance is heavy, so please cache the instance for reuse.
    System.out.println(demoService.sayHello(new User("tc", 18)));

    TimeUnit.MINUTES.sleep(10);
}

3

Desc is Lcom/funnycode/User, and this is the right instance.

Find Reasons for Dubbo-go Errors

Code Location:

protocol/dubbo/impl/hessian.go:120#marshalRequest

Code Implementation:

func marshalRequest(encoder *hessian.Encoder, p DubboPackage) ([]byte, error) {
    service := p.Service
    request := EnsureRequestPayload(p.Body)
    encoder.Encode(DEFAULT_DUBBO_PROTOCOL_VERSION)
    encoder.Encode(service.Path)
    encoder.Encode(service.Version)
    encoder.Encode(service.Method)

    args, ok := request.Params.([]interface{})

    if !ok {
        logger.Infof("request args are: %+v", request.Params)
        return nil, perrors.Errorf("@params is not of type: []interface{}")
    }
    types, err := getArgsTypeList(args)
    if err != nil {
        return nil, perrors.Wrapf(err, " PackRequest(args:%+v)", args)
    }
    encoder.Encode(types)
    for _, v := range args {
        encoder.Encode(v)
    }

    ......
}

From the breakpoint, the types is returned as Object, and no User returned. Then, continue to view the code:

  • protocol/dubbo/impl/hessian.go:394#getArgsTypeList
  • protocol/dubbo/impl/hessian.go:418#getArgType
func getArgType(v interface{}) string {
  // Common type processing

  ......

  default:
    t := reflect.TypeOf(v)
    if reflect.Ptr == t.Kind() {
      t = reflect.TypeOf(reflect.ValueOf(v).Elem())
    }
    switch t.Kind() {
    case reflect.Struct:
      return "java.lang.Object"
    }
    ......
}

java.lang.Object is returned when it is reflect.Struct, and the parameter becomes Object. As a result, the call fails because the Java code depends on this type.

Other Version Validation

Since an error is detected in version 2.7.7, the service provider is switched to Dubbo 2.7.3 to see if errors occur in the former versions. As suggested by the result, the call errors still exist, as shown below:

2020-12-02T21:52:25.945+0800    INFO    getty/listener.go:85    session{session session-closed, Read Bytes: 4586, Write Bytes: 232, Read Pkgs: 0, Write Pkgs: 1} got error{java exception:org.apache.dubbo.rpc.RpcException: Failed to invoke remote proxy method sayHello to registry://127.0.0.1:2181/org.apache.dubbo.registry.RegistryService?application=demoProvider&dubbo=2.0.2&export=dubbo%3A%2F%2F192.168.0.113%3A12345%2Fcom.funnycode.DemoService%3Fanyhost%3Dtrue%26application%3DdemoProvider%26bind.ip%3D192.168.0.113%26bind.port%3D12345%26deprecated%3Dfalse%26dubbo%3D2.0.2%26dynamic%3Dtrue%26generic%3Dfalse%26group%3Dtc%26interface%3Dcom.funnycode.DemoService%26methods%3DsayHello%26pid%3D23889%26register%3Dtrue%26release%3D2.7.3%26revision%3D1.0.0%26side%3Dprovider%26threads%3D200%26timeout%3D60000%26timestamp%3D1606916702204%26version%3D1.0.0&pid=23889&registry=zookeeper&release=2.7.3&timestamp=1606916702193, cause: Not found method "sayHello" in class com.funnycode.DemoServiceImpl.
org.apache.dubbo.rpc.RpcException: Failed to invoke remote proxy method sayHello to registry://127.0.0.1:2181/org.apache.dubbo.registry.RegistryService?application=demoProvider&dubbo=2.0.2&export=dubbo%3A%2F%2F192.168.0.113%3A12345%2Fcom.funnycode.DemoService%3Fanyhost%3Dtrue%26application%3DdemoProvider%26bind.ip%3D192.168.0.113%26bind.port%3D12345%26deprecated%3Dfalse%26dubbo%3D2.0.2%26dynamic%3Dtrue%26generic%3Dfalse%26group%3Dtc%26interface%3Dcom.funnycode.DemoService%26methods%3DsayHello%26pid%3D23889%26register%3Dtrue%26release%3D2.7.3%26revision%3D1.0.0%26side%3Dprovider%26threads%3D200%26timeout%3D60000%26timestamp%3D1606916702204%26version%3D1.0.0&pid=23889&registry=zookeeper&release=2.7.3&timestamp=1606916702193, cause: Not found method "sayHello" in class com.funnycode.DemoServiceImpl.
        at org.apache.dubbo.rpc.proxy.AbstractProxyInvoker.invoke(AbstractProxyInvoker.java:107)
        at org.apache.dubbo.config.invoker.DelegateProviderMetaDataInvoker.invoke(DelegateProviderMetaDataInvoker.java:56)
        at org.apache.dubbo.rpc.protocol.InvokerWrapper.invoke(InvokerWrapper.java:56)
        at org.apache.dubbo.rpc.filter.ExceptionFilter.invoke(ExceptionFilter.java:55)
        at org.apache.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:82)
        at org.apache.dubbo.monitor.support.MonitorFilter.invoke(MonitorFilter.java:92)
        at org.apache.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:82)
        at org.apache.dubbo.rpc.filter.TimeoutFilter.invoke(TimeoutFilter.java:48)
        at org.apache.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:82)
        at org.apache.dubbo.rpc.protocol.dubbo.filter.TraceFilter.invoke(TraceFilter.java:81)
        at org.apache.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:82)
        at org.apache.dubbo.rpc.filter.ContextFilter.invoke(ContextFilter.java:96)
        at org.apache.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:82)
        at org.apache.dubbo.rpc.filter.GenericFilter.invoke(GenericFilter.java:148)
        at org.apache.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:82)
        at org.apache.dubbo.rpc.filter.ClassLoaderFilter.invoke(ClassLoaderFilter.java:38)
        at org.apache.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:82)
        at org.apache.dubbo.rpc.filter.EchoFilter.invoke(EchoFilter.java:41)
        at org.apache.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:82)
        at org.apache.dubbo.rpc.protocol.ProtocolFilterWrapper$CallbackRegistrationInvoker.invoke(ProtocolFilterWrapper.java:157)
        at org.apache.dubbo.rpc.protocol.dubbo.DubboProtocol$1.reply(DubboProtocol.java:152)
        at org.apache.dubbo.remoting.exchange.support.header.HeaderExchangeHandler.handleRequest(HeaderExchangeHandler.java:102)
        at org.apache.dubbo.remoting.exchange.support.header.HeaderExchangeHandler.received(HeaderExchangeHandler.java:193)
        at org.apache.dubbo.remoting.transport.DecodeHandler.received(DecodeHandler.java:51)
        at org.apache.dubbo.remoting.transport.dispatcher.ChannelEventRunnable.run(ChannelEventRunnable.java:57)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
        at java.lang.Thread.run(Thread.java:748)
Caused by: org.apache.dubbo.common.bytecode.NoSuchMethodException: Not found method "sayHello" in class com.funnycode.DemoServiceImpl.
        at org.apache.dubbo.common.bytecode.Wrapper1.invokeMethod(Wrapper1.java)
        at org.apache.dubbo.rpc.proxy.javassist.JavassistProxyFactory$1.doInvoke(JavassistProxyFactory.java:47)
        at org.apache.dubbo.rpc.proxy.AbstractProxyInvoker.invoke(AbstractProxyInvoker.java:84)
        ... 27 more
}, will be closed.

Although this is different from the code in 2.7.7, it can also be seen from the errors that the method cannot be found in the proxy enhancement class. It is highly possible that the reflection cannot find the method, which is also a parameter problem.

Problem Solving

The repair is simply to obtain JavaClassName defined by struct.

case reflect.Struct:
  v, ok := v.(hessian.POJO)
  if ok {
    return v.JavaClassName()
  }
  return "java.lang.Object"

Verification Results

Execute the consumer again, and the operation is normal in both 2.7.7 and 2.7.3 providers. The output is listed below:

[2020-12-03/20:04:06 main.main: client.go: 29] response result: Hello tc
...
[2020-12-03/20:04:09 main.main: client.go: 41] response result: Hello tc You are 18
...
[2020-12-03/20:04:09 main.main: client.go: 48] response result: Hello tc You are 18

Dubbo-go Consumer Configuration

Maybe you have found that the consumer interface in Dubbo-go is called DemoProvider, and then called DemoService in the provider. How does it work normally?

It is related to the references in client.yml. In the configuration file, interface, version, and group are described in detail. Methods can be used to configure method information, such as the timeout.

references:
  "DemoProvider":
    # Multiple registries can be specified with commas to separate. If not specified, register with all registry centers by default.
    registry: "zk1"
    protocol: "dubbo"
    interface: "com.funnycode.DemoService"
    cluster: "failover"
    version: "1.0.0"
    group: "tc"
    methods:
      - name: "SayHello"
        retries: 3
    ......

Global Group and Version Configuration

The following figure shows the configuration file:

# application config
application:
  organization: "dubbogoproxy.com"
  name: "Demo Micro Service"
  module: "dubbogoproxy tc client"
  version: "1.0.0"
  group: "tc"
  owner: "ZX"
  environment: "dev"

references:
  "DemoProvider":
    # Multiple registries can be specified with commas to separate. If not specified, register with all registry centers by default.
    registry: "zk1"
    protocol: "dubbo"
    interface: "com.funnycode.DemoService"
    cluster: "failover"
#    version: "1.0.0"
#    group: "tc"
    methods:
      - name: "SayHello"
        retries: 3

Usually, it must be application that represents the global configuration. However, when starting version and group configured in application, it will not be assigned to the interface. The result of service not found will be reported after the start:

2020-12-03T20:15:42.208+0800    DEBUG   zookeeper/registry.go:237       Create a zookeeper node:/dubbo/com.funnycode.DemoService/consumers/consumer%3A%2F%2F30.11.176.107%2FDemoProvider%3Fapp.version%3D1.0.0%26application%3DDemo+Micro+Service%26async%3Dfalse%26bean.name%3DDemoProvider%26cluster%3Dfailover%26environment%3Ddev%26generic%3Dfalse%26group%3D%26interface%3Dcom.funnycode.DemoService%26ip%3D30.11.176.107%26loadbalance%3D%26methods.SayHello.loadbalance%3D%26methods.SayHello.retries%3D3%26methods.SayHello.sticky%3Dfalse%26module%3Ddubbogoproxy+tc+client%26name%3DDemo+Micro+Service%26organization%3Ddubbogoproxy.com%26owner%3DZX%26pid%3D38692%26protocol%3Ddubbo%26provided-by%3D%26reference.filter%3Dcshutdown%26registry.role%3D0%26release%3Ddubbo-golang-1.3.0%26retries%3D%26side%3Dconsumer%26sticky%3Dfalse%26timestamp%3D1606997742%26version%3D

Version and group are empty. The annotations of version and group under DemoProvider must be opened.

Designation of the Called Method Name

Go Calls Java

When Dubbo-go calls Dubbo, the following errors will occur because method names are capital in Go and lowercase in Java:

2020-12-02T17:10:47.739+0800    INFO    getty/listener.go:87    session{session session-closed, Read Bytes: 924, Write Bytes: 199, Read Pkgs: 0, Write Pkgs: 1} got error{java exception:Fail to decode request due to: java.lang.IllegalArgumentException: Service not found:com.funnycode.DemoService, SayHello
java.lang.IllegalArgumentException: Service not found:com.funnycode.DemoService, SayHello
        at org.apache.dubbo.rpc.protocol.dubbo.DecodeableRpcInvocation.decode(DecodeableRpcInvocation.java:134)
        at org.apache.dubbo.rpc.protocol.dubbo.DecodeableRpcInvocation.decode(DecodeableRpcInvocation.java:80)
        at org.apache.dubbo.remoting.transport.DecodeHandler.decode(DecodeHandler.java:57)
        at org.apache.dubbo.remoting.transport.DecodeHandler.received(DecodeHandler.java:44)
        at org.apache.dubbo.remoting.transport.dispatcher.ChannelEventRunnable.run(ChannelEventRunnable.java:57)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
        at java.lang.Thread.run(Thread.java:748)
}, will be closed.

You may have noticed that there is a dubbo:"sayHello" on the consumer interface, indicating that the method name is sayHello. Thus, the service provider can obtain the method name sayHello.

The three declared methods all are called dubbo:"sayHello" because Java can reload the same method names and Go cannot duplicate method names.

Go Calls Go

The following code can be run normally.

The Provider Interface:

type DemoProvider struct{}

func (p *DemoProvider) SayHello(ctx context.Context, name string) (string, error) {
    return "Hello " + name, nil
}

func (p *DemoProvider) SayHello4(ctx context.Context, user *User) (string, error) {
    return "Hello " + user.Name + " You are " + strconv.Itoa(user.Age), nil
}

func (p *DemoProvider) SayHello5(ctx context.Context, user *User, name string) (string, error) {
    return "Hello " + name + " You are " + strconv.Itoa(user.Age), nil
}

func (p *DemoProvider) Reference() string {
    return "DemoProvider"
}

func (p *DemoProvider) MethodMapper() map[string]string {
    return map[string]string{
        "SayHello": "sayHello",
    }
}

The Consumer Interface:

type DemoProvider struct {
  // Call Java and Go
    SayHello  func(ctx context.Context, name string) (string, error)             `dubbo:"sayHello"`
  // Call Java only
    SayHello2 func(ctx context.Context, user *User) (string, error)              `dubbo:"sayHello"`
    SayHello3 func(ctx context.Context, user *User, name string) (string, error) `dubbo:"sayHello"`
  // Call Go only
    SayHello4 func(ctx context.Context, user *User) (string, error)
    SayHello5 func(ctx context.Context, user *User, name string) (string, error)
}

Start the Service Consumer:

func main() {
    config.Load()
    gxlog.CInfo("\n\n\nstart to test dubbo")

    res, err := demoProvider.SayHello(context.TODO(), "tc")
    if err != nil {
        panic(err)
    }

    gxlog.CInfo("response result: %v\n", res)

    user := &User{
        Name: "tc",
        Age:  18,
    }

    res, err = demoProvider.SayHello4(context.TODO(), user)
    if err != nil {
        panic(err)
    }

    gxlog.CInfo("response result: %v\n", res)

    res, err = demoProvider.SayHello5(context.TODO(), user, "tc")
    if err != nil {
        panic(err)
    }

    gxlog.CInfo("response result: %v\n", res)

    initSignal()
}

Note: With the MethodMapper, sometimes the mapping relations of method names need to be configured. Otherwise, the results are still methods not found.

For example, the request SayHello turns into sayHello in Go because of the configuration dubbo:"sayHello". Then, the service provider becomes the sayHello when configured with MethodMapper. The sayHello exposed in Go and Java is lowercase.

Reasons for Using Hessian2

The default value of the SPI mechanism in Dubbo is hessian2:

@SPI("hessian2")
public interface Serialization {
}

The default value in Dubbo-go:

func NewDubboCodec(reader *bufio.Reader) *ProtocolCodec {
    s, _ := GetSerializerById(constant.S_Hessian2)
    return &ProtocolCodec{
        reader:     reader,
        pkgType:    0,
        bodyLen:    0,
        headerRead: false,
        serializer: s.(Serializer),
    }
}

Hessian Serialization Source Code

You can view the breakpoints on your own. The two languages are nearly the same by comparison. RpcInvocation.getParameterTypesDesc() is the parameter of the method.

  • go code: protocol/dubbo/impl/hessian.go:120#marshalRequest
  • java code: org.apache.dubbo.rpc.protocol.dubbo.DubboCodec#encodeRequestData(org.apache.dubbo.remoting.Channel, org.apache.dubbo.common.serialize.ObjectOutput, java.lang.Object, java.lang.String)

The Method Object of the Dubbo-go Service Provider Must Be a Pointer Object

The previous examples were all copied. This time they are typed out, and the following problem occurred.

If your provider is similar to func (p *DemoProvider) SayHello4(ctx context.Context, user User) (string, error), the following error will occur:

2020-12-03T12:42:32.834+0800    ERROR   getty/listener.go:280   OnMessage panic: reflect: Call using *main.User as type main.User
github.com/apache/dubbo-go/remoting/getty.(*RpcServerHandler).OnMessage.func1

The User in the parameter needs to be changed to *User.

The Method Object of the Dubbo-go Service Consumer Can Be a Non-Pointer Object

SayHello4 func(ctx context.Context, user *User) (string, error)
// or
SayHello4 func(ctx context.Context, user User) (string, error)

Some operations will be performed on pointers during parameter serialization:

t := reflect.TypeOf(v)
if reflect.Ptr == t.Kind() {
  t = reflect.TypeOf(reflect.ValueOf(v).Elem())
}

Complete Code

File Configuration Description

Dubbo-go has three main configuration files:

  • server.yaml: The configuration file of service providers
  • client.yaml: The configuration file of service consumers
  • log.yaml: The log file

If no configuration is done, there will be:

2021/01/11 15:31:41 [InitLog] warn: log configure file name is nil
2021/01/11 15:31:41 [consumerInit] application configure(consumer) file name is nil
2021/01/11 15:31:41 [providerInit] application configure(provider) file name is nil

This is not going to work properly. If you are a service provider, the server.yaml file must be configured. If you are a service consumer, the client.yaml file must be configured. The application should be both the consumer and the provider. Therefore, both files need to be configured normally.

The following output is generated when the service provider starts normally:

2021-01-11T15:36:55.003+0800    INFO    protocol/protocol.go:205        The cached exporter keys is dubbo://:20000/DemoProvider?accesslog=&app.version=1.0.0&application=Demo+Micro+Service&auth=&bean.name=DemoProvider&cluster=failover&environment=dev&execute.limit=&execute.limit.rejected.handler=&group=tc&interface=com.funnycode.DemoService&loadbalance=random&methods.SayHello.loadbalance=random&methods.SayHello.retries=3&methods.SayHello.tps.limit.interval=&methods.SayHello.tps.limit.rate=&methods.SayHello.tps.limit.strategy=&methods.SayHello.weight=0&methods.SayHello4.loadbalance=random&methods.SayHello4.retries=3&methods.SayHello4.tps.limit.interval=&methods.SayHello4.tps.limit.rate=&methods.SayHello4.tps.limit.strategy=&methods.SayHello4.weight=0&methods.SayHello5.loadbalance=random&methods.SayHello5.retries=3&methods.SayHello5.tps.limit.interval=&methods.SayHello5.tps.limit.rate=&methods.SayHello5.tps.limit.strategy=&methods.SayHello5.weight=0&module=dubbogoproxy+tc+client&name=Demo+Micro+Service&organization=dubbogoproxy.com&owner=ZX&param.sign=&registry.role=3&release=dubbo-golang-1.3.0&retries=&serialization=&service.filter=echo%2Ctoken%2Caccesslog%2Ctps%2Cgeneric_service%2Cexecute%2Cpshutdown&side=provider&ssl-enabled=false&timestamp=1610350614&tps.limit.interval=&tps.limit.rate=&tps.limit.rejected.handler=&tps.limit.strategy=&tps.limiter=&version=1.0.0&warmup=100!
2021-01-11T15:36:55.003+0800    INFO    dubbo/dubbo_protocol.go:86      Export service: dubbo://:20000/DemoProvider?accesslog=&app.version=1.0.0&application=Demo+Micro+Service&auth=&bean.name=DemoProvider&cluster=failover&environment=dev&execute.limit=&execute.limit.rejected.handler=&group=tc&interface=com.funnycode.DemoService&loadbalance=random&methods.SayHello.loadbalance=random&methods.SayHello.retries=3&methods.SayHello.tps.limit.interval=&methods.SayHello.tps.limit.rate=&methods.SayHello.tps.limit.strategy=&methods.SayHello.weight=0&methods.SayHello4.loadbalance=random&methods.SayHello4.retries=3&methods.SayHello4.tps.limit.interval=&methods.SayHello4.tps.limit.rate=&methods.SayHello4.tps.limit.strategy=&methods.SayHello4.weight=0&methods.SayHello5.loadbalance=random&methods.SayHello5.retries=3&methods.SayHello5.tps.limit.interval=&methods.SayHello5.tps.limit.rate=&methods.SayHello5.tps.limit.strategy=&methods.SayHello5.weight=0&module=dubbogoproxy+tc+client&name=Demo+Micro+Service&organization=dubbogoproxy.com&owner=ZX&param.sign=&registry.role=3&release=dubbo-golang-1.3.0&retries=&serialization=&service.filter=echo%2Ctoken%2Caccesslog%2Ctps%2Cgeneric_service%2Cexecute%2Cpshutdown&side=provider&ssl-enabled=false&timestamp=1610350614&tps.limit.interval=&tps.limit.rate=&tps.limit.rejected.handler=&tps.limit.strategy=&tps.limiter=&version=1.0.0&warmup=100

Code Reproduction

References

Thanks for reading! Due to the limited space, this was only an introduction! You are welcome to participate in the Dubbo-go 3.0 construction.

About the Author

Tiecheng (GithubID: cityiron) is a member of the Dubbo-go community and Dubbo-go-proxy project leader. He is mainly involved in Dubbo-go 1.5 iteration, Dubbo-go 3.0 service routing, and cloud-native. As an expert in Java and Go, he also focuses on cloud-native and microservices.

0 1 0
Share on

You may also like

Comments

Related Products