dubbo

Wesley13
• 阅读 917

作者 | 铁城   dubbo-go 社区 committer

本文将手把手教你使用 dubbogo 调用 dubbogo 或 dubbo 提供的服务提供方。

前言

本文基于dubbogo 1.5.4 版本

最近开始参与 dubbogo 的一些开发测试,之前都是直接拿samples 的例子验证功能,而这次为了复现一个功能问题,打算从零开始搭建一个 dubbo-go 和 dubbo 调用的工程,踩到了一些新人使用 dubbogo 的坑,把这个过程记录下供大家参考。

通过本文你可以了解到:

  • 如何常规配置 dubbogo 消费方去调用 dubbo 和 dubbogo 服务提供方。
  • 通过一个实际的 BUG 介绍解决问题的思路。

解决问题

1. 准备 dubbo 服务提供者

1)基本定义

定 义 DemoService 接口:

public interface DemoService {

定义 User 对象:

public class User implements Serializable {

2)启动 dubbo 服务提供者

用的dubbo 官方示例代码

public static void main(String[] args) throws IOException {

查看 zookeeper 看是否注册成功:

$ls /dubbo/com.funnycode.DemoService/providers

如上的输出表示服务提供方已经启动。

2. 准备 dubbogo 服务消费者

1)基本定义

定义  User 对象 :

type User struct {

定 义 DemoProvider 接口:

type DemoProvider struct {

2)启动 dubbogo 消费者

func main() {

3. 请求结果分析

1)直接调用

确认问题的存在。

第一个接口的参数是字符串,可以正常返回 [2020-12-03/18:59:12 main.main: client.go: 29] response result: Hello tc
第二、三两个接口存在  User 对象 ,无法调用成功。错误信息如下:

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

错误正如issue 中描述的一模一样,因为错误信息返回到了消费端,可以看到 Java 那边的错误堆栈信息,所以直接去看 DecodeableRpcInvocation.decode#134

2)断点查看

代码如下:

// 反序列化
  • 查 看 MethodDescriptor,即找方法是否存在,存在的话就会设置好 ParameterClasses
  • 如果上面没找到,pts == DubboCodec.EMPTY_CLASS_ARRAY 就会满足条件,进而判断是否是泛化调用或者是 echo 调用,如果都不是则报服务找不到方法错误。
  • desc 是 Ljava/lang/Object ,很明显并没有参数是 Object 的方法,所以必然是会报错的。

补充说明:方法查询。
代码如下:

public MethodDescriptor getMethod(String methodName, String params) {

优点 : 比之前的版本加了方法的元信息缓存起来,不使用反射可以提高效率,可以理解用空间换时间。

4. 解决问题

因为直接撸代码并 hold 不住,所以通过比较来查看问题所在。

1)启动 dubbo 服务消费者

通过 api 模式启动,参考官方例子。启动这个是为了查看 Java 版本的传输内容。

public static void main(String[] args) throws InterruptedException {

desc 肉眼可见的是  Lcom/funnycode/User, 这个就是正确的对象了。

2)查找 dubbogo 为什么不对

代码位置:
protocol/dubbo/impl/hessian.go:120#marshalRequest
代码实现:

func marshalRequest(encoder *hessian.Encoder, p DubboPackage) ([]byte, error) {

断点可以发现,types 返回的时候就已经是 Object 了,没有返回 User,那么继续跟进去查看代码。

  • protocol/dubbo/impl/hessian.go:394#getArgsTypeList

  • protocol/dubbo/impl/hessian.go:418#getArgType

    func getArgType(v interface{}) string {

很明 显当发现是 reflect.Struct 的时候就返回了 java.lang.Object,所以参数就变成了 Object,那么因为 Java 代码那边依赖这个类型所以就调用失败了。

3)其它版本验证

因为反馈是 2.7.7 出错,所以先考虑到在之前的版本是否功能正常,于是把服务提供者切换到 dubbo 2.7.3,发现调用仍然有错误,如下:

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.

虽然和 2.7.7 的代码是不一样的,但是通过错误也能看出来是在代理增强类里面方法找不到,大概率是反射找不到方法,所以归根结底也是参数的问题。

4)修复问题

修复相对简单,就是拿 到 struct 定义的 JavaClassName

case reflect.Struct:

5)验证结果

再次执行消费者,运行(提供方 2.7.7 和 2.7.3)正常,输出如下:

[2020-12-03/20:04:06 main.main: client.go: 29] response result: Hello tc

细节叨叨

1. 如何配置 dubbogo 消费者

细心的你是否已经发现,在 我 dubbogo 的消费端接口叫 DemoProvider,然后发现提供者叫 DemoService,这个又是 如何正常运行的?
实际上和 client.yml 中配置项 references 有关,在配置文件详细说明了 interfaceversiongroup 等,你还可以通过 methods 配置方法的超时时间等信息。

references:

2. 全局的 group 和 version 怎么配置

配置文件如下:

# application config

从使用的习 惯来讲,肯定是 application 表示了全局的配置,但是我发现启动的时候在 application 配置的 versiongroup 并不会赋值给接口,启动会报服务提供方找不到,如下:

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

versiongroup 都是空。必须把 DemoProvider 下的 versiongroup 注释打开。

3. 怎么指定调用的方法名

1)go 调用 java

dubbogo 调用 dubbo,因为 go 是大写的方法名,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

细心 的读者可能已经注意到了,我在消费端的接口声明是有个 dubbo:"sayHello" 的,表示方法名是 sayHello,这样在服务提供方就可以得到 sayHello 这个方法名。
还有我声明的三个方法都指明它们的方法名叫 dubbo:"sayHello",这是因为 Java 可以方法名字一样进行重载,而 go 是不能方法名重复的。

2)go 调用 go

直接贴能跑通的代码。

我的提供者接口:

type DemoProvider struct{}

我的消费者接口:

type DemoProvider struct {

启动服务消费者:

func main() {

这里需要注意 MethodMapper 方法,有时候需要在这个方法中配置方法名的映射关系,否则还是会出现找不到方法的错误。
比如因为配置 dubbo:"sayHello",所以在 go 里面请求 SayHello 变成了 sayHello,那么服务提供方通过 MethodMapper 方法配置后使得提供方也是 sayHello,这样 go 和 java 下暴露的都是小写的 sayHello

4. 为什么会用 hessian2

老司机都懂,在 dubbo 中 SPI 机制的默认值就是 hessian2

@SPI("hessian2")

而在 dubbo-go 中:

func NewDubboCodec(reader *bufio.Reader) *ProtocolCodec {

5. hessian 序列化源码

可以自行断点查看,两边基本上一样,我也是通过两边比出来的,RpcInvocation.getParameterTypesDesc() 就是方法的参数.

  • g o 代码 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)

6. dubbogo 服务提供者的方法对象需要是指针对象

之前的例子都是 copy 的,这次是纯手打的,才发现了这个问题。
如果 你的提供类似:func (p *DemoProvider) SayHello4(ctx context.Context, user User) (string, error),那么会出现如下错误:

2020-12-03T12:42:32.834+0800 ERROR getty/listener.go:280 OnMessage panic: reflect: Call using *main.User as type main.User

参数里面 的 User 需要改成 *User

7. dubbogo 服务消费者的方法对象可以是非指针对象

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

因为在参数序列化的时候会对指针做操作:

t := reflect.TypeOf(v)

完整代码

8. 配置文件说明

dubbogo 主要有三个配置文件:

  • server.yaml 服务提供方的配置文件
  • client.yaml 服务消费方的配置文件
  • log.yaml 日志文件

如果你什么都不配置,会出现:

2021/01/11 15:31:41 [InitLog] warn: log configure file name is nil

这样是没法正常使用的。如果你是服务提供方,必须要配置 server.yaml 文件,如果你是服务消费方,必须要配置 client.yaml,实际我们的应用应该既是消费者又是提供者,所以往往两个文件都是需要配置的。
服务提供方正常启动是会有如下输出的:

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!

9. 复现代码

参考

篇幅有限,就介绍到这里。欢迎有兴趣的同学点击【阅读原文】参与 dubbogo3.0 的建设,感谢阅读。 如果你有任何疑问,欢迎钉钉扫码加入交流群【或搜索钉钉群号 31363295】:

作者简介

铁城(GithubID cityiron) ,dubbo-go 社区 committer,主要参与 dubbo-go 1.5 版本迭代、 dubbo-go 3.0 服务路由和云原生方面工作、以及 dubbo-go-proxy 项目负责人。擅长使用 Java/Go 语言,专注于云原生和微服务等技术方向。
戳原文,立即参与 dubbogo3.0 的建设!

点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
待兔 待兔
4个月前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
Jacquelyn38 Jacquelyn38
3年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
Stella981 Stella981
3年前
Opencv中Mat矩阵相乘——点乘、dot、mul运算详解
Opencv中Mat矩阵相乘——点乘、dot、mul运算详解2016年09月02日00:00:36 \牧野(https://www.oschina.net/action/GoToLink?urlhttps%3A%2F%2Fme.csdn.net%2Fdcrmg) 阅读数:59593
Stella981 Stella981
3年前
Go 版本入 Dubbo 生态一周年:已和 Spring Cloud、gRPC 互通
本文作者:o\\\\0去年5月,阿里开源的高性能RPC框架Dubbo从ASF毕业并晋升顶级项目,同时,还宣布Go语言版本的Dubbogo正式加入Dubbo官方生态。经过一年的发展,Dubbogo在技术和社区运营方面都已经有了不错的成绩。Dubbogo是Dubbo的完整Go语言实现,在功能实现和技术路
Stella981 Stella981
3年前
Android蓝牙连接汽车OBD设备
//设备连接public class BluetoothConnect implements Runnable {    private static final UUID CONNECT_UUID  UUID.fromString("0000110100001000800000805F9B34FB");
Stella981 Stella981
3年前
Google地球出现“无法连接到登录服务器(错误代码:c00a0194)”解决方法
Google地球出现“无法连接到登录服务器(错误代码:c00a0194)”解决方法参考文章:(1)Google地球出现“无法连接到登录服务器(错误代码:c00a0194)”解决方法(https://www.oschina.net/action/GoToLink?urlhttps%3A%2F%2Fwww.codeprj.com%2Fblo
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Python进阶者 Python进阶者
10个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这