结果缓存
结果缓存,用于加速热门数据的访问速度,Dubbo提供声明式缓存,以减少用户加缓存的工作量。
2.1.0以上版本支持
示例代码:https://github.com/alibaba/dubbo/tree/master/dubbo-test/dubbo-test-examples/src/main/java/com/alibaba/dubbo/examples/cache
lru 基于最近最少使用原则删除多余缓存,保持最热的数据被缓存。
threadlocal 当前线程缓存,比如一个页面渲染,用到很多portal,每个portal都要去查用户信息,通过线程缓存,可以减少这种多余访问。
jcache 与JSR107集成,可以桥接各种缓存实现。
缓存类型可扩展,参见:CacheFactory扩展点
配置如:
<dubbo:reference
interface="com.foo.BarService"
cache="lru"
/> |
或:
<dubbo:reference
interface="com.foo.BarService">
<dubbo:method
name="findBar"
cache="lru"
/>
</dubbo:reference> |
泛化引用
泛接口调用方式主要用于客户端没有API接口及模型类元的情况,参数及返回值中的所有POJO均用Map表示,通常用于框架集成,比如:实现一个通用的服务测试框架,可通过GenericService调用所有服务实现。
<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"); |
泛化实现
泛接口实现方式主要用于服务器端没有API接口及模型类元的情况,参数及返回值中的所有POJO均用Map表示,通常用于框架集成,比如:实现一个通用的远程服务Mock框架,可通过实现GenericService接口处理所有服务请求。
<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,即可使用。
<dubbo:reference
id="memberService"
interface="com.xxx.MemberService"
/> |
MemberService
memberService = ctx.getBean("memberService");
//
远程服务引用
EchoService
echoService = (EchoService) memberService; //
强制转型为EchoService
String
status = echoService.$echo("OK");
//
回声测试可用性
assert(status.equals("OK"))
|
上下文信息
上下文中存放的是当前调用过程中所需的环境信息。
所有配置信息都将转换为URL的参数,参见《配置项一览表》中的“对应URL参数”一列。
注意
RpcContext是一个ThreadLocal的临时状态记录器,当接收到RPC请求,或发起RPC请求时,RpcContext的状态都会变化。
比如:A调B,B再调C,则B机器上,在B调C之前,RpcContext记录的是A调B的信息,在B调C之后,RpcContext记录的是B调C的信息。
(1) 服务消费方
xxxService.xxx(); // 远程调用
boolean
isConsumerSide = RpcContext.getContext().isConsumerSide();
//
本端是否为消费端,这里会返回true
String
serverIP = RpcContext.getContext().getRemoteHost();
//
获取最后一次调用的提供方IP地址
String
application = RpcContext.getContext().getUrl().getParameter("application");
//
获取当前服务配置信息,所有配置信息都将转换为URL的参数
//
...
yyyService.yyy();
//
注意:每发起RPC调用,上下文状态会变化
//
...
|
(2) 服务提供方
public
class
XxxServiceImpl implements
XxxService {
public
void
xxx() { //
服务方法实现
boolean
isProviderSide = RpcContext.getContext().isProviderSide();
//
本端是否为提供端,这里会返回true
String
clientIP = RpcContext.getContext().getRemoteHost();
//
获取调用方IP地址
String
application = 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() { //
服务方法实现
String
index = RpcContext.getContext().getAttachment("index");
//
获取客户端隐式传入的参数,用于框架集成,不建议常规业务使用
//
...
}
}
|
异步调用
基于NIO的非阻塞实现并行调用,客户端不需要启动多线程即可完成并行调用多个远程服务,相对多线程开销较小。
2.0.6及其以上版本支持
配置声明:
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"
/> |
或
<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"
/> |
参数回调
参数回调方式与调用本地callback或listener相同,只需要在Spring的配置文件中声明哪个参数是callback类型即可,Dubbo将基于长连接生成反向代理,这样就可以从服务器端调用客户端逻辑。
2.0.6及其以上版本支持
代码参见:https://github.com/alibaba/dubbo/tree/master/dubbo-test/dubbo-test-examples/src/main/java/com/alibaba/dubbo/examples/callback
(1) 共享服务接口:
服务接口示例:
CallbackService.java
package
com.callback;
public
interface
CallbackService {
void
addListener(String key, CallbackListener listener);
}
|
CallbackListener.java
package
com.callback;
public
interface
CallbackListener {
void
changed(String msg);
}
|
(2) 服务提供者:
服务提供者接口实现示例:
CallbackServiceImpl.java
package
com.callback.impl;
import
java.text.SimpleDateFormat;
import
java.util.Date;
import
java.util.Map;
import
java.util.concurrent.ConcurrentHashMap;
import
com.callback.CallbackListener;
import
com.callback.CallbackService;
public
class
CallbackServiceImpl implements
CallbackService {
private
final
Map<String, CallbackListener> listeners
= new
ConcurrentHashMap<String, CallbackListener>();
public
CallbackServiceImpl() {
Thread
t = new
Thread(new
Runnable() {
public
void
run() {
while(true)
{
try
{
for(Map.Entry<String,
CallbackListener> entry : listeners.entrySet()){
try
{
entry.getValue().changed(getChanged(entry.getKey()));
}
catch
(Throwable t) {
listeners.remove(entry.getKey());
}
}
Thread.sleep(5000);
//
定时触发变更通知
}
catch
(Throwable t) { //
防御容错
t.printStackTrace();
}
}
}
});
t.setDaemon(true);
t.start();
}
public
void
addListener(String key, CallbackListener listener)
{
listeners.put(key,
listener);
listener.changed(getChanged(key));
//
发送变更通知
}
private
String getChanged(String key) {
return
"Changed: "
+ new
SimpleDateFormat("yyyy-MM-dd
HH:mm:ss").format(new
Date());
}
}
|
服务提供者配置示例:
<bean
id="callbackService"
class="com.callback.impl.CallbackServiceImpl"
/>
<dubbo:service
interface="com.callback.CallbackService"
ref="callbackService"
connections="1"
callbacks="1000">
<dubbo:method
name="addListener">
<dubbo:argument
index="1"
callback="true"
/>
<!--也可以通过指定类型的方式-->
<!--<dubbo:argument
type="com.demo.CallbackListener" callback="true"
/>-->
</dubbo:method>
</dubbo:service>
|
(3) 服务消费者:
服务消费者配置示例:
consumer.xml
<dubbo:reference
id="callbackService"
interface="com.callback.CallbackService"
/>
|
服务消费者调用示例:
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("classpath:consumer.xml"); context.start(); CallbackService callbackService = (CallbackService) context.getBean("callbackService"); callbackService.addListener("http://10.20.160.198/wiki/display/dubbo/foo.bar", new CallbackListener(){ public void changed(String msg) { System.out.println("callback1:" + msg); } }); |
事件通知
在调用之前,调用之后,出现异常时,会触发oninvoke, onreturn,
onthrow三个事件,可以配置当事件发生时,通知哪个类的哪个方法。
支持版本:2.0.7之后
(1) 服务提供者与消费者共享服务接口:
IDemoService.java
interface
IDemoService {
public
Person get(int
id);
}
|
(2) 服务提供者实现:
DemoServiceImpl.java
class
NormalDemoService implements
IDemoService {
public
Person get(int
id) {
return
new
Person(id, "charles`son",
4);
}
}
|
(3) 服务提供者配置:
provider.xml
<dubbo:application name="rpc-callback-demo" />
<dubbo:registry address="http://10.20.160.198/wiki/display/dubbo/10.20.153.186"
/>
<bean id="demoService" class="com.alibaba.dubbo.callback.implicit.NormalDemoService"
/>
<dubbo:service interface="com.alibaba.dubbo.callback.implicit.IDemoService"
ref="demoService" version="1.0.0"
group="cn"/> |
(4) 服务消费者Callback接口及实现:
Nofify.java
interface
Nofify {
public
void
onreturn(Person msg, Integer id);
public
void
onthrow(Throwable ex, Integer id);
}
|
class
NofifyImpl implements
Nofify {
public
Map<Integer, Person> ret = new
HashMap<Integer, Person>();
public
Map<Integer, Throwable> errors = new
HashMap<Integer, Throwable>();
public
void
onreturn(Person msg, Integer id) {
System.out.println("onreturn:"
+ msg);
ret.put(id,
msg);
}
public
void
onthrow(Throwable ex, Integer id) {
errors.put(id,
ex);
}
}
|
(5) 服务消费者Callback接口及实现:
consumer.xml
注:
callback与async功能正交分解:
async=true,表示结果是否马上返回.
onreturn 表示是否需要回调.
组合情况:(async=false 默认)
异步回调模式:async=true onreturn="xxx"
同步回调模式:async=false onreturn="xxx"
异步无回调 :async=true
同步无回调 :async=false
(6) TEST CASE:
Test.java
<bean
id
="demoCallback"
class
= "com.alibaba.dubbo.callback.implicit.NofifyImpl"
/>
<dubbo:reference
id="demoService"
interface="com.alibaba.dubbo.callback.implicit.IDemoService"
version="1.0.0"
group="cn"
>
<dubbo:method
name="get"
async="true"
onreturn
= "demoCallback.onreturn"
onthrow="demoCallback.onthrow"
/>
</dubbo:reference>
|
|