作者 | 铁城 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 {String sayHello(String name);
String sayHello(User user);
String sayHello(User user, String name);
}
定义 User
对象:
public class User implements Serializable {
private String name;
private int age;
......
}
2)启动 dubbo 服务提供者
用的 dubbo 官网示例代码:
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();}
查看 zookeeper 看是否注册胜利:
$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]
如上的输入示意服务提供方曾经启动。
2. 筹备 dubbogo 服务消费者
1)根本定义
定义 User
对象:
type User struct {
Name string
Age int
}
func (User) JavaClassName() string {return "com.funnycode.User"}
定义 DemoProvider
接口:
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)启动 dubbogo 消费者
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. 申请后果剖析
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
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.
谬误正如 issue 中形容的截然不同,因为错误信息返回到了生产端,能够看到 Java 那边的谬误堆栈信息,所以间接去看 DecodeableRpcInvocation.decode#134
。
2)断点查看
代码如下:
// 反序列化
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);
}
......
}
}
- 查看
MethodDescriptor
,即找办法是否存在,存在的话就会设置好ParameterClasses
。 - 如果下面没找到,
pts == DubboCodec.EMPTY_CLASS_ARRAY
就会满足条件,进而判断是否是泛化调用或者是 echo 调用,如果都不是则报服务找不到办法谬误。 - desc 是
Ljava/lang/Object
,很显著并没有参数是 Object 的办法,所以必然是会报错的。
补充阐明:办法查问。
**
代码如下:
public MethodDescriptor getMethod(String methodName, String params) {Map<String, MethodDescriptor> methods = descToMethods.get(methodName);
if (CollectionUtils.isNotEmptyMap(methods)) {return methods.get(params);
}
return null;
}
长处 :比之前的版本加了办法的元信息缓存起来,不应用反射能够提高效率,能够了解用空间换工夫。
4. 解决问题
因为间接撸代码并 hold 不住,所以通过比拟来查看问题所在。
1)启动 dubbo 服务消费者
通过 api 模式启动,参考官网例子。启动这个是为了查看 Java 版本的传输内容。
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);
}
desc 肉眼可见的是 Lcom/funnycode/User
,这个就是正确的对象了。
2)查找 dubbogo 为什么不对
代码地位:protocol/dubbo/impl/hessian.go:120#marshalRequest
代码实现:
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)
}
......
}
断点能够发现,types 返回的时候就曾经是 Object
了,没有返回 User
,那么持续跟进去查看代码。
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"
}
......
}
很显著当发现是 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®istry=zookeeper&release=2.7.3×tamp=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®istry=zookeeper&release=2.7.3×tamp=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.
尽管和 2.7.7 的代码是不一样的,然而通过谬误也能看进去是在代理加强类外面办法找不到,大概率是反射找不到办法,所以归根结底也是参数的问题。
4)修复问题
修复绝对简略,就是拿到 struct
定义的 JavaClassName
。
case reflect.Struct:
v, ok := v.(hessian.POJO)
if ok {return v.JavaClassName()
}
return "java.lang.Object"
5)验证后果
再次执行消费者,运行(提供方 2.7.7 和 2.7.3)失常,输入如下:
[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
细节叨叨
1. 如何配置 dubbogo 消费者
仔细的你是否曾经发现,在我 dubbogo 的生产端接口叫 DemoProvider
,而后发现提供者叫 DemoService
,这个又是如何失常运行的?
实际上和 client.yml
中配置项 references
无关,在配置文件具体阐明了 interface
,version
,group
等,你还能够通过 methods 配置办法的超时工夫等信息。
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. 全局的 group 和 version 怎么配置
配置文件如下:
# 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
从应用的习惯来讲,必定是 application
示意了全局的配置,然而我发现启动的时候在 application
配置的 version
和 group
并不会赋值给接口,启动会报服务提供方找不到,如下:
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
和 group
都是空。必须把 DemoProvider
下的 version
和 group
正文关上。
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
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.
仔细的读者可能曾经留神到了,我在生产端的接口申明是有个 dubbo:"sayHello"
的,示意办法名是 sayHello,这样在服务提供方就能够失去 sayHello 这个办法名。
还有我申明的三个办法都指明它们的办法名叫 dubbo:"sayHello"
,这是因为 Java 能够办法名字一样进行重载,而 go 是不能办法名反复的。
2)go 调用 go
间接贴能跑通的代码。
我的提供者接口:
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",}
}
我的消费者接口:
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)
}
启动服务消费者:
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()}
这里须要留神 MethodMapper
办法,有时候须要在这个办法中配置办法名的映射关系,否则还是会呈现找不到办法的谬误。
比方因为配置 dubbo:"sayHello"
,所以在 go 外面申请 SayHello
变成了 sayHello
,那么服务提供方通过 MethodMapper
办法配置后使得提供方也是 sayHello
,这样 go 和 java 下裸露的都是小写的 sayHello
。
4. 为什么会用 hessian2
老司机都懂,在 dubbo 中 SPI 机制的默认值就是 hessian2
@SPI("hessian2")
public interface Serialization {}
而在 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 序列化源码
能够自行断点查看,两边基本上一样,我也是通过两边比进去的,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)
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
github.com/apache/dubbo-go/remoting/getty.(*RpcServerHandler).OnMessage.func1
参数外面的 User
须要改成 *User
。
7. dubbogo 服务消费者的办法对象能够是非指针对象
SayHello4 func(ctx context.Context, user *User) (string, error)
// or
SayHello4 func(ctx context.Context, user User) (string, error)
因为在参数序列化的时候会对指针做操作:
t := reflect.TypeOf(v)
if reflect.Ptr == t.Kind() {t = reflect.TypeOf(reflect.ValueOf(v).Elem())
}
残缺代码
8. 配置文件阐明
dubbogo 次要有三个配置文件:
- server.yaml 服务提供方的配置文件
- client.yaml 服务生产方的配置文件
- log.yaml 日志文件
如果你什么都不配置,会呈现:
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
这样是没法失常应用的。如果你是服务提供方,必须要配置 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¶m.sign=®istry.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×tamp=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¶m.sign=®istry.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×tamp=1610350614&tps.limit.interval=&tps.limit.rate=&tps.limit.rejected.handler=&tps.limit.strategy=&tps.limiter=&version=1.0.0&warmup=100
9. 复现代码
- https://github.com/cityiron/java_study/tree/master/dubbo2.7.7/dg-issue900
- https://github.com/cityiron/golang_study/tree/master/dubbogo/1.5.4/arg-bug
参考
- https://dubbo.apache.org/zh/docs/v2.7/user/configuration/api/
- https://github.com/apache/dubbo-go/issues/257
篇幅无限,就介绍到这里。欢送有趣味的同学参加 dubbogo3.0 的建设,感激浏览。如果你有任何疑难,欢送钉钉搜寻群号:31363295,退出交换群。
作者简介
铁城 (GithubID cityiron),dubbo-go 社区 committer,次要参加 dubbo-go 1.5 版本迭代、dubbo-go 3.0 服务路由和云原生方面工作、以及 dubbo-go-proxy 我的项目负责人。善于应用 Java/Go 语言,专一于云原生和微服务等技术方向。