泛化引用
<dubbo:reference id="barService" interface="com.foo.BarService" generic="true" /> |
GenericService barService = (GenericService) applicationContext.getBean("barService"); Object result = barService.$invoke("sayHello", new String[] { "java.lang.String" }, new Object[] { "World" }); |
import com.alibaba.dubbo.rpc.service.GenericService; ...
// 引用远程服务 ReferenceConfig<GenericService> reference = new ReferenceConfig<GenericService>(); // 该实例很重量,里面封装了所有与注册中心及服务提供方连接,请缓存 reference.setInterface("com.xxx.XxxService"); // 弱类型接口名 reference.setVersion("1.0.0"); reference.setGeneric(true); // 声明为泛化接口
GenericService genericService = reference.get(); // 用com.alibaba.dubbo.rpc.service.GenericService可以替代所有接口引用
// 基本类型以及Date,List,Map等不需要转换,直接调用 Object result = genericService.$invoke("sayHello", new String[] {"java.lang.String"}, new Object[] {"world"});
// 用Map表示POJO参数,如果返回值为POJO也将自动转成Map Map<String, Object> person = new HashMap<String, Object>(); person.put("name", "xxx"); person.put("password", "yyy"); Object result = genericService.$invoke("findPerson", new String[]{"com.xxx.Person"}, new Object[]{person}); // 如果返回POJO将自动转成Map
... |
假设存在POJO如:
package com.xxx; public class PersonImpl implements Person { private String name; private String password; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getPassword() { return password; } public void setPassword(String password) { this.password= password; } } |
则POJO数据:
Person person = new PersonImpl(); person.setName("xxx"); person.setPassword("yyy"); |
可用下面Map表示:
Map<String, Object> map = new HashMap<String, Object>(); map.put("class", "com.xxx.PersonImpl"); // 注意:如果参数类型是接口,或者List等丢失泛型,可通过class属性指定类型。 map.put("name", "xxx"); map.put("password", "yyy"); |
泛化实现
<bean id="genericService" class="com.foo.MyGenericService" /> <dubbo:service interface="com.foo.BarService" ref="genericService" /> |
package com.foo; public class MyGenericService implements GenericService {
public Object $invoke(String methodName, String[] parameterTypes, Object[] args) throws GenericException { if ("sayHello".equals(methodName)) { return "Welcome " + args[0]; } }
} | |
... GenericService xxxService = new XxxGenericService(); // 用com.alibaba.dubbo.rpc.service.GenericService可以替代所有接口实现
ServiceConfig<GenericService> service = new ServiceConfig<GenericService>(); // 该实例很重量,里面封装了所有与注册中心及服务提供方连接,请缓存 service.setInterface("com.xxx.XxxService"); // 弱类型接口名 service.setVersion("1.0.0"); service.setRef(xxxService); // 指向一个通用服务实现
// 暴露及注册服务 service.export();
回声测试 回声测试用于检测服务是否可用,回声测试按照正常请求流程执行,能够测试整个调用是否通畅,可用于监控。所有服务自动实现EchoService接口,只需将任意服务引强制转型为EchoService,即可使用。
上下文信息 RpcContext是一个ThreadLocal的临时状态记录器,当接收到RPC请求,或发起RPC请求时,RpcContext的状态都会变化。比如A调用B,B再调用C,则B机器上,在B调用C之前,RpcContext记录的是A调用B的信息,在B调用C之后,RpcContext记录的是B调用C。
(1)服务消费方
|
(2)服务提供方
public class XxxServiceImpl implements XxxService {
public void xxx(){ // 服务方法实现
boolean isProviderSide =RpcContext.getContext().isProviderSide(); // 本端是否为提供端,这里会返回true
StringclientIP = RpcContext.getContext().getRemoteHost(); // 获取调用方IP地址
Stringapplication =RpcContext.getContext().getUrl().getParameter("application");
// 获取当前服务配置信息,所有配置信息都将转换为URL的参数
//...
yyyService.yyy();// 注意:每发起RPC调用,上下文状态会变化
boolean isProviderSide = RpcContext.getContext().isProviderSide();// 此时本端变成消费端,这里会返回false
//...
}
}
隐式传参
注意path, group, version, dubbo, token, timeout几个key有特殊处理,请使用其它key值。
(1)服务消费方
RpcContext.getContext().setAttachment("index", "1"); // 隐式传参,后面的远程调用都会隐式将这些参数发送到服务器端,类似cookie,用于框架集成,不建议常规业务使用 xxxService.xxx(); // 远程调用 // ... |
【注】 setAttachment设置的KV,在完成下面一次远程调用会被清空。即多次远程调用要多次设置。
(2)服务提供方
public class XxxServiceImpl implements XxxService {
public void xxx(){ // 服务方法实现
Stringindex = RpcContext.getContext().getAttachment("index");
// 获取客户端隐式传入的参数,用于框架集成,不建议常规业务使用
//...
}
}
consumer.xml
<dubbo:reference id="fooService" interface="com.alibaba.foo.FooService"> <dubbo:method name="findFoo" async="true" /> </dubbo:reference> <dubbo:reference id="barService" interface="com.alibaba.bar.BarService"> <dubbo:method name="findBar" async="true" /> </dubbo:reference> |
调用代码:
fooService.findFoo(fooId); // 此调用会立即返回null Future<Foo> fooFuture = RpcContext.getContext().getFuture(); // 拿到调用的Future引用,当结果返回后,会被通知和设置到此Future。
barService.findBar(barId); // 此调用会立即返回null Future<Bar> barFuture = RpcContext.getContext().getFuture(); // 拿到调用的Future引用,当结果返回后,会被通知和设置到此Future。
// 此时findFoo和findBar的请求同时在执行,客户端不需要启动多线程来支持并行,而是借助NIO的非阻塞完成。
Foo foo = fooFuture.get(); // 如果foo已返回,直接拿到返回值,否则线程wait住,等待foo返回后,线程会被notify唤醒。 Bar bar = barFuture.get(); // 同理等待bar返回。
// 如果foo需要5秒返回,bar需要6秒返回,实际只需等6秒,即可获取到foo和bar,进行接下来的处理。 |
你也可以设置是否等待消息发出:(异步总是不等待返回)
- sent="true" 等待消息发出,消息发送失败将抛出异常。
- sent="false" 不等待消息发出,将消息放入IO队列,即刻返回。
<dubbo:method name="findFoo" async="true" sent="true" /> |
如果你只是想异步,完全忽略返回值,可以配置return="false",以减少Future对象的创建和管理成本:
<dubbo:method name="findFoo" async="true" return="false" /> |
本地调用
本地调用 ,使用了Injvm协议,是一个伪协议,它不开启端口,不发起远程调用,只在JVM内直接关联,但执行dubbo的Filter链。
Define injvm protocol:
<dubbo:protocol name="injvm" /> |
Set default protocol:
<dubbo:provider protocol="injvm" /> |
Set service protocol:
<dubbo:service protocol="injvm" /> |
Use injvm first:
<dubbo:consumer injvm="true" .../> <dubbo:provider injvm="true" .../> |
或
<dubbo:reference injvm="true" .../> <dubbo:service injvm="true" .../> | |||
| 注意:服务暴露与服务引用都需要声明injvm="true" |
|
从 dubbo 2.2.0 开始,每个服务默认都会在本地暴露;在引用服务的时候,默认优先引用本地服务;如果希望引用远程服务可以使用一下配置强制引用远程服务。
... <dubbo:reference ... scope="remote" /> ... |
最后欢迎大家访问我的个人网站:1024s