dubbo-go 白话文 | go 和 java 互通有无
本文从一个 BUG 入手,手把手教你 dubbogo 调用 dubbogo 或 dubbo 服务
一、前言
昨天邹部长在群里@我让看一个关于 dubbogo 调用 dubbo 报错的问题,问题issue地址,于是我跑了 dubbogo + dubbbo 的测试代码来定位这个问题,因为之前也没跨语言调用,从零开始搭建,踩到了一些新人使用dubbogo的坑,把这个过程记录下供大家参考。
二、解决问题
2.1 准备 dubbo 服务提供者
2.1.1 基本定义
定义 DemoService
接口:
1public interface DemoService {
2
3 String sayHello(String name);
4
5 String sayHello(User user);
6
7 String sayHello(User user, String name);
8
9}
定义 User
对象:
1public class User implements Serializable {
2
3 private String name;
4
5 private int age;
6
7 ......
8}
2.1.2 启动 dubbo 服务提供者
用的 dubbo 官方示例代码:
1public static void main(String[] args) throws IOException {
2 // 服务实现
3 DemoService demoService = new DemoServiceImpl();
4
5 // 当前应用配置
6 ApplicationConfig application = new ApplicationConfig();
7 application.setName("demoProvider");
8
9 // 连接注册中心配置
10 RegistryConfig registry = new RegistryConfig();
11 registry.setAddress("127.0.0.1:2181");
12 registry.setProtocol("zookeeper");
13 registry.setUsername("");
14 registry.setPassword("");
15
16 // 服务提供者协议配置
17 ProtocolConfig protocol = new ProtocolConfig();
18 protocol.setName("dubbo");
19 protocol.setPort(12345);
20 protocol.setThreads(200);
21
22 // 注意:ServiceConfig为重对象,内部封装了与注册中心的连接,以及开启服务端口
23
24 // 服务提供者暴露服务配置
25 ServiceConfig service = new ServiceConfig<>(); // 此实例很重,封装了与注册中心的连接,请自行缓存,否则可能造成内存和连接泄漏
26 service.setApplication(application);
27 service.setRegistry(registry); // 多个注册中心可以用setRegistries()
28 service.setProtocol(protocol); // 多个协议可以用setProtocols()
29 service.setInterface(DemoService.class);
30 service.setRef(demoService);
31 service.setVersion("1.0.0");
32 service.setGroup("tc");
33 service.setTimeout(60 * 1000);
34
35 // 暴露及注册服务
36 service.export();
37
38 System.in.read();
39}
查看 zookeeper 看是否注册成功:
1$ls /dubbo/com.funnycode.DemoService/providers
2[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]
如上的输出表示服务提供方已经启动。
2.2 准备 dubbo 服务消费者
2.2.1 基本定义
定义 User
对象:
1type User struct {
2 Name string
3 Age int
4}
5
6func (User) JavaClassName() string {
7 return "com.funnycode.User"
8}
定义 DemoProvider
接口:
1type DemoProvider struct {
2 SayHello func(ctx context.Context, name string) (string, error) `dubbo:"sayHello"`
3 SayHello2 func(ctx context.Context, user User) (string, error) `dubbo:"sayHello"`
4 SayHello3 func(ctx context.Context, user User, name string) (string, error) `dubbo:"sayHello"`
5}
6
7func (p *DemoProvider) Reference() string {
8 return "DemoProvider"
9}
2.2.2 启动 dubbogo 消费者
1func main() {
2 config.Load()
3 gxlog.CInfo("\n\n\nstart to test dubbo")
4
5 res, err := demoProvider.SayHello(context.TODO(), "tc")
6 if err != nil {
7 panic(err)
8 }
9
10 gxlog.CInfo("response result: %v\n", res)
11
12 user := User{
13 Name: "tc",
14 Age: 18,
15 }
16
17 res, err = demoProvider.SayHello2(context.TODO(), user)
18 if err != nil {
19 panic(err)
20 }
21
22 gxlog.CInfo("response result: %v\n", res)
23
24 res, err = demoProvider.SayHello3(context.TODO(), user, "tc")
25 if err != nil {
26 panic(err)
27 }
28
29 gxlog.CInfo("response result: %v\n", res)
30
31 initSignal()
32}
2.3 请求结果分析
2.3.1 直接调用
确认问题的存在
第一个接口的参数是字符串,可以正常返回 [2020-12-03/18:59:12 main.main: client.go: 29] response result: Hello tc
第二、三两个接口是 User
对象,无法调用成功。错误信息如下:
12020-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
2 at org.apache.dubbo.rpc.protocol.dubbo.DecodeableRpcInvocation.decode(DecodeableRpcInvocation.java:134)
3 at org.apache.dubbo.rpc.protocol.dubbo.DecodeableRpcInvocation.decode(DecodeableRpcInvocation.java:80)
4 at org.apache.dubbo.remoting.transport.DecodeHandler.decode(DecodeHandler.java:57)
5 at org.apache.dubbo.remoting.transport.DecodeHandler.received(DecodeHandler.java:44)
6 at org.apache.dubbo.remoting.transport.dispatcher.ChannelEventRunnable.run(ChannelEventRunnable.java:57)
7 at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
8 at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
9 at java.lang.Thread.run(Thread.java:748)
10}, will be closed.
正如 issue 中描述的一模一样,因为直接给出了 Java 那边的错误信息,所以直接去看 DecodeableRpcInvocation.decode#134
。
2.3.2 断点查看
代码如下:
1public class DecodeableRpcInvocation extends RpcInvocation implements Codec, Decodeable {
2 public Object decode(Channel channel, InputStream input) throws IOException {
3 ......
4 if (serviceDescriptor != null) {
5 MethodDescriptor methodDescriptor = serviceDescriptor.getMethod(getMethodName(), desc);
6 if (methodDescriptor != null) {
7 pts = methodDescriptor.getParameterClasses();
8 this.setReturnTypes(methodDescriptor.getReturnTypes());
9 }
10 }
11 if (pts == DubboCodec.EMPTY_CLASS_ARRAY) {
12 if (!RpcUtils.isGenericCall(path, getMethodName()) && !RpcUtils.isEcho(path, getMethodName())) {
13 throw new IllegalArgumentException("Service not found:" + path + ", " + getMethodName());
14 }
15 pts = ReflectUtils.desc2classArray(desc);
16 }
17 ......
18 }
19}
查看
MethodDescriptor
,即找方法是否存在,存在的话就会设置好ParameterClasses
如果上面没找到,
pts == DubboCodec.EMPTY_CLASS_ARRAY
就会满足条件,进而判断是否是泛化调用或者是 echo 调用,如果都不是则报服务找不到方法错误desc 是
Ljava/lang/Object
,很明显并没有参数是 Object 的方法,所以必然是会报错的
补充说明:
方法查询
代码如下:
1public MethodDescriptor getMethod(String methodName, String params) {
2 Map methods = descToMethods.get(methodName);
3 if (CollectionUtils.isNotEmptyMap(methods)) {
4 return methods.get(params);
5 }
6 return null;
7}
优点:
比之前的版本加了方法的元信息缓存起来,不使用反射可以提高效率,可以理解用空间换时间。
2.4 解决问题
因为直接撸代码并 hold 不住,所以通过比较来查看问题所在。
2.4.1 启动 dubbo 服务消费者
通过 api 模式启动,参考官方例子。启动这个是为了查看 Java 版本的传输内容。
1public static void main(String[] args) throws InterruptedException {
2 // 当前应用配置
3 ApplicationConfig application = new ApplicationConfig();
4 application.setName("demoProvider2");
5
6 // 连接注册中心配置
7 RegistryConfig registry = new RegistryConfig();
8 registry.setAddress("127.0.0.1:2181");
9 registry.setProtocol("zookeeper");
10 registry.setUsername("");
11 registry.setPassword("");
12 // 注意:ReferenceConfig为重对象,内部封装了与注册中心的连接,以及与服务提供方的连接
13
14 // 引用远程服务
15 ReferenceConfig reference
16 = new ReferenceConfig<>(); // 此实例很重,封装了与注册中心的连接以及与提供者的连接,请自行缓存,否则可能造成内存和连接泄漏
17 reference.setApplication(application);
18 reference.setRegistry(registry); // 多个注册中心可以用setRegistries()
19 reference.setInterface(DemoService.class);
20 reference.setVersion("1.0.0");
21 reference.setGroup("tc");
22 reference.setCheck(true);
23 reference.setTimeout(1000 * 60);
24
25 // 和本地bean一样使用xxxService
26 DemoService demoService = reference.get(); // 注意:此代理对象内部封装了所有通讯细节,对象较重,请缓存复用
27 System.out.println(demoService.sayHello(new User("tc", 18)));
28
29 TimeUnit.MINUTES.sleep(10);
30}
2.4.2 查找dubbogo为什么不对
感谢提 issue 的同学,把代码已经贴出来了
代码位置:
protocol/dubbo/impl/hessian.go:120#marshalRequest
代码实现:
1func marshalRequest(encoder *hessian.Encoder, p DubboPackage) ([]byte, error) {
2 service := p.Service
3 request := EnsureRequestPayload(p.Body)
4 encoder.Encode(DEFAULT_DUBBO_PROTOCOL_VERSION)
5 encoder.Encode(service.Path)
6 encoder.Encode(service.Version)
7 encoder.Encode(service.Method)
8
9 args, ok := request.Params.([]interface{})
10
11 if !ok {
12 logger.Infof("request args are: %+v", request.Params)
13 return nil, perrors.Errorf("@params is not of type: []interface{}")
14 }
15 types, err := getArgsTypeList(args)
16 if err != nil {
17 return nil, perrors.Wrapf(err, " PackRequest(args:%+v)", args)
18 }
19 encoder.Encode(types)
20 for _, v := range args {
21 encoder.Encode(v)
22 }
23
24 ......
25}
断点可以发现,types 返回的时候就已经是 Object
了,没有返回 User
,那么继续跟进去查看代码。
protocol/dubbo/impl/hessian.go:394#getArgsTypeList
protocol/dubbo/impl/hessian.go:418#getArgType
1func getArgType(v interface{}) string {
2 // 常见的类型处理
3
4 ......
5
6 default:
7 t := reflect.TypeOf(v)
8 if reflect.Ptr == t.Kind() {
9 t = reflect.TypeOf(reflect.ValueOf(v).Elem())
10 }
11 switch t.Kind() {
12 case reflect.Struct:
13 return "java.lang.Object"
14 }
15 ......
16}
很明显当发现是 reflect.Struct
的时候就返回了 java.lang.Object
,所以参数就变成了 Object
,那么因为 Java 代码那边依赖这个类型所以就调用失败了。
2.4.3 其它版本验证
因为反馈是 2.7.7 出错,所以先考虑到在之前的版本是否功能正常,于是把服务提供者切换到 dubbo 2.7.3,发现调用仍然有错误,如下:
12020-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®istry=zookeeper&release=2.7.3×tamp=1606916702193, cause: Not found method "sayHello" in class com.funnycode.DemoServiceImpl.
2org.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®istry=zookeeper&release=2.7.3×tamp=1606916702193, cause: Not found method "sayHello" in class com.funnycode.DemoServiceImpl.
3 at org.apache.dubbo.rpc.proxy.AbstractProxyInvoker.invoke(AbstractProxyInvoker.java:107)
4 at org.apache.dubbo.config.invoker.DelegateProviderMetaDataInvoker.invoke(DelegateProviderMetaDataInvoker.java:56)
5 at org.apache.dubbo.rpc.protocol.InvokerWrapper.invoke(InvokerWrapper.java:56)
6 at org.apache.dubbo.rpc.filter.ExceptionFilter.invoke(ExceptionFilter.java:55)
7 at org.apache.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:82)
8 at org.apache.dubbo.monitor.support.MonitorFilter.invoke(MonitorFilter.java:92)
9 at org.apache.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:82)
10 at org.apache.dubbo.rpc.filter.TimeoutFilter.invoke(TimeoutFilter.java:48)
11 at org.apache.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:82)
12 at org.apache.dubbo.rpc.protocol.dubbo.filter.TraceFilter.invoke(TraceFilter.java:81)
13 at org.apache.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:82)
14 at org.apache.dubbo.rpc.filter.ContextFilter.invoke(ContextFilter.java:96)
15 at org.apache.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:82)
16 at org.apache.dubbo.rpc.filter.GenericFilter.invoke(GenericFilter.java:148)
17 at org.apache.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:82)
18 at org.apache.dubbo.rpc.filter.ClassLoaderFilter.invoke(ClassLoaderFilter.java:38)
19 at org.apache.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:82)
20 at org.apache.dubbo.rpc.filter.EchoFilter.invoke(EchoFilter.java:41)
21 at org.apache.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:82)
22 at org.apache.dubbo.rpc.protocol.ProtocolFilterWrapper$CallbackRegistrationInvoker.invoke(ProtocolFilterWrapper.java:157)
23 at org.apache.dubbo.rpc.protocol.dubbo.DubboProtocol$1.reply(DubboProtocol.java:152)
24 at org.apache.dubbo.remoting.exchange.support.header.HeaderExchangeHandler.handleRequest(HeaderExchangeHandler.java:102)
25 at org.apache.dubbo.remoting.exchange.support.header.HeaderExchangeHandler.received(HeaderExchangeHandler.java:193)
26 at org.apache.dubbo.remoting.transport.DecodeHandler.received(DecodeHandler.java:51)
27 at org.apache.dubbo.remoting.transport.dispatcher.ChannelEventRunnable.run(ChannelEventRunnable.java:57)
28 at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
29 at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
30 at java.lang.Thread.run(Thread.java:748)
31Caused by: org.apache.dubbo.common.bytecode.NoSuchMethodException: Not found method "sayHello" in class com.funnycode.DemoServiceImpl.
32 at org.apache.dubbo.common.bytecode.Wrapper1.invokeMethod(Wrapper1.java)
33 at org.apache.dubbo.rpc.proxy.javassist.JavassistProxyFactory$1.doInvoke(JavassistProxyFactory.java:47)
34 at org.apache.dubbo.rpc.proxy.AbstractProxyInvoker.invoke(AbstractProxyInvoker.java:84)
35 ... 27 more
36}, will be closed.
虽然和 2.7.7 的代码是不一样的,但是通过错误也能看出来是在代理增强类里面方法找不到,大概率是反射找不到方法,所以归根结底也是参数的问题。
2.4.4 修复问题
修复相对简单,就是拿到 struct
定义的 JavaClassName
。
1case reflect.Struct:
2 v, ok := v.(hessian.POJO)
3 if ok {
4 return v.JavaClassName()
5 }
6 return "java.lang.Object"
2.4.3 验证结果
再次执行消费者,运行(提供方 2.7.7 和 2.7.3)正常,输出如下:
1[2020-12-03/20:04:06 main.main: client.go: 29] response result: Hello tc
2...
3[2020-12-03/20:04:09 main.main: client.go: 41] response result: Hello tc You are 18
4...
5[2020-12-03/20:04:09 main.main: client.go: 48] response result: Hello tc You are 18
三、细节叨叨
3.1 如何配置 dubbgo 消费者
细心的你是否已经发现,在我 dubbogo 的消费端接口叫 DemoProvider
,然后发现提供者叫 DemoService
,这个又是如何正常运行的?
实际上和 client.yml
中配置项 references
有关,在配置文件详细说明了 interface
,version
,group
等,你还可以通过 methods 配置方法的超时时间等信息。
1references:
2 "DemoProvider":
3 # 可以指定多个registry,使用逗号隔开;不指定默认向所有注册中心注册
4 registry: "zk1"
5 protocol: "dubbo"
6 interface: "com.funnycode.DemoService"
7 cluster: "failover"
8 version: "1.0.0"
9 group: "tc"
10 methods:
11 - name: "SayHello"
12 retries: 3
13 ......
3.2 全局的 group 和 version 怎么配置
配置文件如下:
1# application config
2application:
3 organization: "dubbogoproxy.com"
4 name: "Demo Micro Service"
5 module: "dubbogoproxy tc client"
6 version: "1.0.0"
7 group: "tc"
8 owner: "ZX"
9 environment: "dev"
10
11references:
12 "DemoProvider":
13 # 可以指定多个registry,使用逗号隔开;不指定默认向所有注册中心注册
14 registry: "zk1"
15 protocol: "dubbo"
16 interface: "com.funnycode.DemoService"
17 cluster: "failover"
18# version: "1.0.0"
19# group: "tc"
20 methods:
21 - name: "SayHello"
22 retries: 3
从使用的习惯来讲,肯定是 application
表示了全局的配置,但是我发现启动的时候在 application
配置的 version
和 group
并不会赋值给接口,启动会报服务提供方找不到,如下:
12020-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
和 group
都是空。必须把 DemoProvider
下的 version
和 group
注释打开。这个细节后面分析配置文件解析的时候会展开说明。
3.3 怎么指定调用的方法名
3.3.1 go 调用 java
dubbogo 调用 dubbo,因为 go 是大写的方法名,java 里面是小写的方法名,所以会出现如下错误:
12020-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
2java.lang.IllegalArgumentException: Service not found:com.funnycode.DemoService, SayHello
3 at org.apache.dubbo.rpc.protocol.dubbo.DecodeableRpcInvocation.decode(DecodeableRpcInvocation.java:134)
4 at org.apache.dubbo.rpc.protocol.dubbo.DecodeableRpcInvocation.decode(DecodeableRpcInvocation.java:80)
5 at org.apache.dubbo.remoting.transport.DecodeHandler.decode(DecodeHandler.java:57)
6 at org.apache.dubbo.remoting.transport.DecodeHandler.received(DecodeHandler.java:44)
7 at org.apache.dubbo.remoting.transport.dispatcher.ChannelEventRunnable.run(ChannelEventRunnable.java:57)
8 at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
9 at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
10 at java.lang.Thread.run(Thread.java:748)
11}, will be closed.
关联的issue
细心的读者可能已经注意到了,我在消费端的接口声明是有个 dubbo:"sayHello"
的,表示方法名是 sayHello,这样在服务提供方就可以得到 sayHello 这个方法名。
还有我声明的三个方法都指明它们的方法名叫 dubbo:"sayHello"
,这是因为 Java 可以方法名字一样进行重载,而 go 是不能方法名重复的。
扯淡:在 go 里面看到 java 的错误熟悉的味道,奇怪的感觉
3.3.2 go 调用 go
直接贴能跑通的代码
我的提供者接口:
1type DemoProvider struct{}
2
3func (p *DemoProvider) SayHello(ctx context.Context, name string) (string, error) {
4 return "Hello " + name, nil
5}
6
7func (p *DemoProvider) SayHello4(ctx context.Context, user *User) (string, error) {
8 return "Hello " + user.Name + " You are " + strconv.Itoa(user.Age), nil
9}
10
11func (p *DemoProvider) SayHello5(ctx context.Context, user *User, name string) (string, error) {
12 return "Hello " + name + " You are " + strconv.Itoa(user.Age), nil
13}
14
15func (p *DemoProvider) Reference() string {
16 return "DemoProvider"
17}
18
19func (p *DemoProvider) MethodMapper() map[string]string {
20 return map[string]string{
21 "SayHello": "sayHello",
22 }
23}
我的消费者使用:
1type DemoProvider struct {
2 // 调用 java 和 go
3 SayHello func(ctx context.Context, name string) (string, error) `dubbo:"sayHello"`
4 // 只调用 java
5 SayHello2 func(ctx context.Context, user *User) (string, error) `dubbo:"sayHello"`
6 SayHello3 func(ctx context.Context, user *User, name string) (string, error) `dubbo:"sayHello"`
7 // 只调用 go
8 SayHello4 func(ctx context.Context, user *User) (string, error)
9 SayHello5 func(ctx context.Context, user *User, name string) (string, error)
10}
11
1func main() {
2 config.Load()
3 gxlog.CInfo("\n\n\nstart to test dubbo")
4
5 res, err := demoProvider.SayHello(context.TODO(), "tc")
6 if err != nil {
7 panic(err)
8 }
9
10 gxlog.CInfo("response result: %v\n", res)
11
12 user := &User{
13 Name: "tc",
14 Age: 18,
15 }
16
17 res, err = demoProvider.SayHello4(context.TODO(), user)
18 if err != nil {
19 panic(err)
20 }
21
22 gxlog.CInfo("response result: %v\n", res)
23
24 res, err = demoProvider.SayHello5(context.TODO(), user, "tc")
25 if err != nil {
26 panic(err)
27 }
28
29 gxlog.CInfo("response result: %v\n", res)
30
31 initSignal()
32}
这里主要注意 MethodMapper
方法,像我请求 SayHello
变成了 sayHello
,需要在这个方法中配置这个映射关系,否则还是会找不到方法。
3.4 为什么会用 hessian2
老司机都懂,SPI机制的默认值就是 hessian2
1@SPI("hessian2")
2public interface Serialization {
3}
3.5 hessian序列化源码
可以自行断点查看,两边基本上一样,我也是通过两边比出来的,RpcInvocation.getParameterTypesDesc() 就是方法的参数
go 代码
protocol/dubbo/impl/hessian.go:120#marshalRequest
java 代码
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)
3.6 dubbogo 服务提供者的方法对象需要是指针对象
之前的例子都是 copy 的,这次是纯手打的,才发现了这个问题。
如果你的提供类似:func (p *DemoProvider) SayHello4(ctx context.Context, user User) (string, error)
,那么会出现如下错误:
12020-12-03T12:42:32.834+0800 ERROR getty/listener.go:280 OnMessage panic: reflect: Call using *main.User as type main.User
2github.com/apache/dubbo-go/remoting/getty.(*RpcServerHandler).OnMessage.func1
参数里面的 User
需要改成 *User
。
3.7 dubbogo 服务消费者的方法对象可以是非指针对象
1SayHello4 func(ctx context.Context, user *User) (string, error)
2// or
3SayHello4 func(ctx context.Context, user User) (string, error)
4
因为在参数序列化的时候会对指针做操作:
1t := reflect.TypeOf(v)
2if reflect.Ptr == t.Kind() {
3 t = reflect.TypeOf(reflect.ValueOf(v).Elem())
4}
3.8 繁琐的配置简化
查看我另一篇文章 [dubbo配置简化] 文章未上传,后续补充。
3.9 复现代码
https://github.com/cityiron/java_study/tree/master/dubbo2.7.7
https://github.com/cityiron/golang_study/tree/master/dubbogo/client
四、参考
https://dubbo.apache.org/zh/docs/v2.7/user/configuration/api/
推荐阅读