dubbo-go Vernacular | Simple use cases to build dubbogo and dubbo from scratch

Head picture.png

Author | Tiecheng dubbo-go Community Committer
Source | Alibaba Cloud Native Official Account

This article will teach you how to use dubbogo to call the service provider provided by dubbogo or dubbo.

Preface

This article is based on dubbogo version 1.5.4 .

Recently I started to participate in some development and testing of dubbogo. Before, I used  the examples of samples to verify the function. This time, in order to reproduce a functional problem, I plan to build a dubbo-go and dubbo call project from scratch. Some new people use it. The pit of dubbogo, record this process for your reference.

Through this article, you can learn:

  • How to routinely configure dubbogo consumers to call dubbo and dubbogo service providers.
  • Introduce the idea of ​​solving the problem through an actual BUG.

Solve the problem

1. Prepare dubbo service provider

1) Basic definition

Defined DemoServiceinterfaces:

public interface DemoService {

    String sayHello(String name);

    String sayHello(User user);

    String sayHello(User user, String name);

}

Defined Userobjects:

public class User implements Serializable {

    private String name;

    private int age;

    ......
}

2) Start dubbo service provider

The official dubbo sample code used :

public static void main(String[] args) throws IOException {
    // 服务实现
    DemoService demoService = new DemoServiceImpl();

    // 当前应用配置
    ApplicationConfig application = new ApplicationConfig();
    application.setName("demoProvider");

    // 连接注册中心配置
    RegistryConfig registry = new RegistryConfig();
    registry.setAddress("127.0.0.1:2181");
    registry.setProtocol("zookeeper");
    registry.setUsername("");
    registry.setPassword("");

    // 服务提供者协议配置
    ProtocolConfig protocol = new ProtocolConfig();
    protocol.setName("dubbo");
    protocol.setPort(12345);
    protocol.setThreads(200);

    // 注意:ServiceConfig为重对象,内部封装了与注册中心的连接,以及开启服务端口

    // 服务提供者暴露服务配置
    ServiceConfig<DemoService> service = new ServiceConfig<>(); // 此实例很重,封装了与注册中心的连接,请自行缓存,否则可能造成内存和连接泄漏
    service.setApplication(application);
    service.setRegistry(registry); // 多个注册中心可以用setRegistries()
    service.setProtocol(protocol); // 多个协议可以用setProtocols()
    service.setInterface(DemoService.class);
    service.setRef(demoService);
    service.setVersion("1.0.0");
    service.setGroup("tc");
    service.setTimeout(60 * 1000);

    // 暴露及注册服务
    service.export();

    System.in.read();
}

Check zookeeper to see if the registration is successful:

$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 above output indicates that the service provider has started.

2. Prepare dubbogo to serve consumers

1) Basic definition

Define the  Userobject:

type User struct {
    Name string
    Age  int
}

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

Defined DemoProviderinterfaces:

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"
}

2) Start dubbogo 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()
}

3. Request result analysis

1) Direct call

Confirm the existence of the problem.

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

The second and third interfaces have  Userobjects and cannot be called successfully. The error message is 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.

The error  is exactly the same as described in the issue , because the error information is returned to the consumer, you can see the error stack information on the Java side, so go directly to it DecodeableRpcInvocation.decode#134.

2) Breakpoint view

code show as below:

// 反序列化
public class DecodeableRpcInvocation extends RpcInvocation implements Codec, Decodeable {
    public Object decode(Channel channel, InputStream input) throws IOException {
      ......
      if (serviceDescriptor != null) {
          // 方法描述里面根据方法名查找
          MethodDescriptor methodDescriptor = serviceDescriptor.getMethod(getMethodName(), desc);
          if (methodDescriptor != null) {
              pts = methodDescriptor.getParameterClasses();
              this.setReturnTypes(methodDescriptor.getReturnTypes());
          }
      }
      // 表示没有找到方法        
      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);
      }
      ......
    }
}
  • Check MethodDescriptor, that is, find whether the method exists, if it exists, it will be set ParameterClasses.
  • If the above is not found, the pts == DubboCodec.EMPTY_CLASS_ARRAYconditions will be met, and then it will be judged whether it is a generalized call or an echo call. If neither is found, the service cannot find a method error.
  • desc Yes Ljava/lang/Object, obviously there is no method whose parameter is Object, so an error is bound to be reported.

Supplementary note: method query.
** The
code is as follows:

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 meta information of the method is cached, and the efficiency can be improved by not using reflection, and it can be understood that space is exchanged for time.

1.jpg

4. Solve the problem

Because you can't hold the code directly, you can check the problem by comparing.

1) Start dubbo service consumer

Start by api mode, refer to the official example. This is activated to view the transmission content of the Java version.

public static void main(String[] args) throws InterruptedException {
    // 当前应用配置
    ApplicationConfig application = new ApplicationConfig();
    application.setName("demoProvider2");

    // 连接注册中心配置
    RegistryConfig registry = new RegistryConfig();
    registry.setAddress("127.0.0.1:2181");
    registry.setProtocol("zookeeper");
    registry.setUsername("");
    registry.setPassword("");
    // 注意:ReferenceConfig为重对象,内部封装了与注册中心的连接,以及与服务提供方的连接

    // 引用远程服务
    ReferenceConfig<DemoService> reference
        = new ReferenceConfig<>(); // 此实例很重,封装了与注册中心的连接以及与提供者的连接,请自行缓存,否则可能造成内存和连接泄漏
    reference.setApplication(application);
    reference.setRegistry(registry); // 多个注册中心可以用setRegistries()
    reference.setInterface(DemoService.class);
    reference.setVersion("1.0.0");
    reference.setGroup("tc");
    reference.setCheck(true);
    reference.setTimeout(1000 * 60);

    // 和本地bean一样使用xxxService
    DemoService demoService = reference.get(); // 注意:此代理对象内部封装了所有通讯细节,对象较重,请缓存复用
    System.out.println(demoService.sayHello(new User("tc", 18)));

    TimeUnit.MINUTES.sleep(10);
}

2.png

desc To the naked eye  Lcom/funnycode/User, this is the right object.

2) Why is it wrong to find dubbogo

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

Code:

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)
    }

    ......
}

Breakpoints can be found, when the types of returns is already Object, and did not return User, then continue with the inside look at the code.

  • protocol/dubbo/impl/hessian.go:394#getArgsTypeList
  • protocol/dubbo/impl/hessian.go:418#getArgType
func getArgType(v interface{}) string {
  // 常见的类型处理

  ......

  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"
    }
    ......
}

Obviously when found reflect.Structwhen he returned java.lang.Object, so the argument becomes Object, because then the Java code over there so I rely on this type of call failed.

3) Verification of other versions

Because the feedback was an error in 2.7.7, we first considered whether the previous version was functioning normally, so we switched the service provider to dubbo 2.7.3 and found that the call still had an error, as follows:

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 the code is different from 2.7.7, it can be seen from the error that the method cannot be found in the proxy enhancement class, and there is a high probability that reflection cannot find the method, so in the final analysis it is also a parameter problem.

4) Fix the problem

Repair is relatively simple, is to get structdefined JavaClassName.

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

5) Verification result

Execute the consumer again, the operation (provider 2.7.7 and 2.7.3) is normal, and the output is as follows:

[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

Talk about the details

1. How to configure dubbogo consumer

Carefully, have you discovered that the consumer interface of my dubbogo is called DemoProvider, and then the provider is called DemoService. How does this work normally?

In fact, and client.ymlthe configuration items referencesrelated to the configuration file specifies interface, version, groupetc., you can also pass information and other methods to configure the timeout method.

references:
  "DemoProvider":
    # 可以指定多个registry,使用逗号隔开;不指定默认向所有注册中心注册
    registry: "zk1"
    protocol: "dubbo"
    interface: "com.funnycode.DemoService"
    cluster: "failover"
    version: "1.0.0"
    group: "tc"
    methods:
      - name: "SayHello"
        retries: 3
    ......

2. How to configure the global group and version

The configuration file is as follows:

# 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":
    # 可以指定多个registry,使用逗号隔开;不指定默认向所有注册中心注册
    registry: "zk1"
    protocol: "dubbo"
    interface: "com.funnycode.DemoService"
    cluster: "failover"
#    version: "1.0.0"
#    group: "tc"
    methods:
      - name: "SayHello"
        retries: 3

From the habit of using terms, certainly applicationrepresents a global configuration, but I found that when activated in applicationthe configuration versionand groupthe provider can not be found and will not be assigned to the interface, start the service will be reported as follows:

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

versionAnd groupthey are empty. You must be DemoProviderunder versionand groupcomment open.

3. How to specify the name of the called method

1) go adjustment java

dubbogo calls dubbo, because go is an uppercase method name, and java is a lowercase method name, so the following error will occur:

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.

Careful readers may have noticed, I declare the consumer side of the interface is there dubbo:"sayHello", showing the method name is sayHello, so that service providers can get sayHello method name.

In addition, the three methods I declared all specify their method names dubbo:"sayHello". This is because Java can be overloaded with the same method name, and go cannot duplicate method names.

2) go adjustment go

Directly paste the code that can run through.

My 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",
    }
}

My consumer interface:

type DemoProvider struct {
  // 调用 java 和 go
    SayHello  func(ctx context.Context, name string) (string, error)             `dubbo:"sayHello"`
  // 只调用 java
    SayHello2 func(ctx context.Context, user *User) (string, error)              `dubbo:"sayHello"`
    SayHello3 func(ctx context.Context, user *User, name string) (string, error) `dubbo:"sayHello"`
  // 只调用 go
    SayHello4 func(ctx context.Context, user *User) (string, error)
    SayHello5 func(ctx context.Context, user *User, name string) (string, error)
}

Start 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()
}

It should be noted that MethodMapperthe method sometimes need to configure the mapping between the method name in this method, or otherwise not found error method will appear.

Because such configuration dubbo:"sayHello", so go inside the request SayHellobecomes sayHello, the service provider by MethodMapperthe method such that the provider is configured sayHelloso that the lower and go are exposed java lowercase sayHello.

4. Why use hessian2

Old drivers know that the default value of the SPI mechanism in dubbo is hessian2

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

And 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),
    }
}

5. Hessian serialization source code

You can check by your own breakpoints. The two sides are basically the same. I also compare them by comparing them. RpcInvocation.getParameterTypesDesc() is the method parameter.

  • 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)

6. The method object of the dubbogo service provider needs to be a pointer object

The previous examples were all copied, this time it was pure hand-playing, and I discovered this problem.
If your offer is similar to:, func (p *DemoProvider) SayHello4(ctx context.Context, user User) (string, error)then the following error will appear:

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

Inside the parameters Userneed to be changed *User.

7. The method object of dubbogo 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)

Because the pointer is operated on when the parameter is serialized:

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

Complete code

8. Configuration file description

There are three main configuration files for dubbogo:

  • server.yaml service provider configuration file
  • client.yaml service consumer configuration file
  • log.yaml log file

If you do not configure anything, it will appear:

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 cannot be used normally. If you are a service provider, you must configure the server.yaml file. If you are a service consumer, you must configure client.yaml. Actually, our application should be both a consumer and a provider, so often both files are required. Configured.

The normal startup of the service provider will have the following output:

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

9. Reproduce the code

reference

Space is limited, so let’s stop here. Interested students are welcome to participate in the construction of dubbogo3.0 , thanks for reading.

About the Author

Iron City (GithubID cityiron) , dubbo-go community committer, mainly involved in dubbo-go 1.5 version iteration, dubbo-go 3.0 service routing and cloud native work, and dubbo-go-proxy project leader. Good at using Java/Go language, focusing on technology directions such as cloud native and microservices.

Guess you like

Origin blog.51cto.com/13778063/2595687