22.本地存根消费者通过创建实现一个服务接口的实例,可以在执行远程调用前拿到远程调用的代理实例,进而可以在远程调用前、后添加一些操作,在出现异常后进行一些容错处理。
这个使用场景,可以调用前作数据参数校验、做ThreadLocal缓存(这个线程操作多次调用这个服务,而且结果是一样的,就可以缓存起来,第二次就不用再远程调用)、出错后如何处理等等;其实就是相当于一个AOP一样的功能如
服务接口:
public interface User { String getUserInfoByName(String name);
}
提供者和平常一样正常实现接口并注册能力到注册中心:
消费者创建一个服务接口实现类:public class UserSub implements User {
private final User user;
///必须提供一个传服务接口的构造器,最终传入的是代理对象,用于调用远程服务/*/ public UserSub(User user){
this.user = user; }
public String getUserInfoByName(String name) {
try { //远程调用之前
System.out.println("远程调用之前"); //远程调用
String userInfoByName = this.user.getUserInfoByName(name); //远程调用之后
System.out.println("远程调用之后"+userInfoByName); return userInfoByName;
}catch (Exception e) { return null;
} }
}
配置消费者:(stub指向刚才的类)
使用:和平常一样
User user = (User) context.getBean("user");System.out.println(user.getUserInfoByName("啊哈"));
结果:(提供者是直接返回name)
远程调用之前
远程调用之后啊哈啊哈 (远程调用结果)
21.事件通知用于消费者reference下的method 属性
oninvoke:调用服务之前;onreturn:调用方法之后;
onthrow:出异常后;它们的值是spring容器中某个bean的名称.方法名
如:
要注意的是TestNotify的三个方法参数问题
先看服务接口:
///两个参数/*/public interface CallBackService extends Serializable{
String addListener( String name, CallBackListener callBackListener);}
public class TestNotify {
///oninvoke方法的参数必须与服务接口一致,包括顺序/*/ public void oninvoke(String name, CallBackListener listener){
System.out.println("-------oninvoke-----------"); }
///onreturn方法的第一个参数是服务接口的返回类型,服务接口的参数要么全无/*/ public void onreturn(String result){
System.out.println("----------onreturn--------"); }
///onreturn方法的第一个参数是服务接口的返回类型,服务接口的参数要么全有,但顺序也要一致/*/ public void onreturn2(String result,String name, CallBackListener listener){
System.out.println("----------onreturn--------"); }
///onthrow方法的第一个参数是异常类型,服务接口的参数要么全无/*/
public void onthrow(Throwable ex) { System.out.println("--------onthrow----------");
} ///onthrow方法的第一个参数是异常类型,服务接口的参数要么全有,但顺序也要一致/*/
public void onthrow2(Throwable ex,String name, CallBackListener listener) { System.out.println("--------onthrow----------");
}}
值得注意的是:假如配置onthrow 方法,也配置了oninvoke或onreturn,如果oninvoke或onreturn的参数不对(或这两个方法里面报异常),将会把oninvoke或onreturn转为调用onthrow,这样就有可能出现明明是服务调用正常却跑进了onthrow
比如: ///oninvoke方法的参数必须与服务接口一致,包括顺序/*/
public void oninvoke(String name, CallBackListener listener){ System.out.println("-------oninvoke-----------"+ 1/0);
}
结果是:-------------
回调:222(服务调用前逻辑)--------onthrow--------- - (服务调用前通知,明明是服务正常调用,去跑进了onthrow,却又没发现代码有抛异常)
回调:CallBackService:CallBackServiceImpl(服务调用)--------onthrow----------(服务调用后通知)
-------------
20.参数回调
定义一些服务接口,参数传一个回调监听器,这样消费者调用服务是,可以在服务端的回调客服端的代码,比如在提供者根据参数,符合条件才执行回调;补充:就是提供者某个入参为一个抽象的一个接口,而具体的实现由消费者实现,这样把消费者写的代码逻可以在服务端执行,也就是实现一种RPC的广义多态
原理:服务端检查到某个服务接口的某个方法的某个参数是回调的,会通过注册通行,在消费者暴露一个接口给服务端调用( 需要在provider中配置回调参数,这些配置信息会通过registry传递到consumer中 )例子:
///正常的一个服务接口,有个回调参数callBackListener/*/
public interface CallBackService extends Serializable{ void addListener(String name, CallBackListener callBackListener);
}
///回调参数接口/*/public interface CallBackListener extends Serializable {
void listner(String name);}
提供者:
public class CallBackServiceImpl implements CallBackService { public void addListener(String name, CallBackListener callBackListener) {
name += "CallBackServiceImpl"; callBackListener.listner(name);//调用回调
}}
提供者配置回调参数(指定哪个方法第几个参数为callBack=true):
消费者正常配置:
消费服务CallBackService sbs = (CallBackService) context.getBean("sbs");
CallBackListener listener = new CallBackListener() { public void listner(String name) {
System.out.println("回调:"+name); //2 }
};listener.listner("222") ; //3
sbs.addListener("CallBackService:", listener); //1
回调:222回调:CallBackService:CallBackServiceImpl
注意: 提供者执行了这个代码 callBackListener.listner(name);//调用回调,不管后面有没有报错、超时,消费这个的回调函数一样被调用
如果把提供者改为:提供者:
public void addListener(String name, CallBackListener callBackListener) { name += "CallBackServiceImpl";
callBackListener.listner(name); int a =1/0;
}
消费者不变,结果为:
回调:222
回调:CallBackService:CallBackServiceImplException in thread "main" java.lang.ArithmeticException: / by zero
at com.ned.rpc.CallBackServiceImpl.addListener(CallBackServiceImpl.java:18) at com.alibaba.dubbo.common.bytecode.Wrapper5.invokeMethod(Wrapper5.java)
at com.alibaba.dubbo.rpc.proxy.javassist.JavassistProxyFactory$1.doInvoke(JavassistProxyFactory.java:47) at com.alibaba.dubbo.rpc.proxy.AbstractProxyInvoker.invoke(AbstractProxyInvoker.java:76)
19.异步调用
在reference获取其 method上使用 async=true,当调用方法发出去后,立即响应null,通过Future来获取异步的响应;
提供者:
public String getUserInfoByName(String name) { String test = RpcContext.getContext().getAttachment("test");
return userService.getUserInfoByName(test+"getUserInfoByName"+name); }
public String getUserInfoByName2(String name) {
String test = RpcContext.getContext().getAttachment("test"); return userService.getUserInfoByName(test+"getUserInfoByName2"+name);
}
消费者:
调用: User2 user2 = (User2) context.getBean("user2");
RpcContext.getContext().setAttachment("test", "测试Rpcontext"); System.out.println(user2.getUserInfoByName("555")); //1
System.out.println(user2.getUserInfoByName2("555")); //2
Future
结果:(前两个null是异步立即返回null,第三个null是提供者getUserInfoByName2中获取隐形参数)
null
nullnullgetUserInfoByName2555 (这个其实是2注释的远程调用的结果,同时RpcContext中第一次远程调用的结果已经配情况)
========================================================================
再调用: User2 user2 = (User2) context.getBean("user2");
RpcContext.getContext().setAttachment("test", "测试Rpcontext"); System.out.println(user2.getUserInfoByName("555"));
Future
System.out.println(future.get());
nullnull
18.上下文信息隐式参数每一次远程调用后都会被清空
17.回声测试消费者从容器中获取服务bean实例强转为 EchoService这个类型,调用其唯一方法Object $echo(Object var1);
使用(通过$Echo方法发出去什么内容,响应回来什么内容,这个就可以测试服务提供是否正常): User3 user = (User3) context.getBean("user");//这个是一个服务实例
EchoService echoService = (EchoService) user;//转为EchoService类型 Object ok = echoService.$echo("响应我回来就成功了");
System.out.println(ok);输出:
响应我回来就成功了
package com.alibaba.dubbo.rpc.service;
public interface EchoService {
Object $echo(Object var1); }
16.泛化调用提供者跟其他一样,不同在于消费者。消费者reference标签上使用generic="true" 的属性,而interface的接口在消费者是不存在的。
A.参数为基本简单类型消费者配置:
b.给reference配置interface,注意这个com.ned.generic.GenericApi在是 接口的全称,但这接口在消费者是不存在的;c.给reference配置 generic为true,标识泛化调用
使用:
a.从容器中获取一个bean,名称为上面配置的Id属性,强转为GenericServiceGenericService genericService = (GenericService) context.getBean("genericService");
b.调用genericService唯一的一个方法$invoke(); Object testGeneric = genericService.$invoke("testGeneric", new String[]{"java.lang.String"}, new Object[]{"啊哈"});
结果与正常的调用一样
public interface GenericService {
///
/* 泛化调用/*
/* @param method 方法名,如:findPerson,如果有重载方法,需带上参数列表,如:findPerson(java.lang.String)/* @param parameterTypes 参数类型
/* @param args 参数列表/* @return 返回值
/* @throws Throwable 方法抛出的异常/*/
Object $invoke(String method, String[] parameterTypes, Object[] args) throws GenericException;
}
B.参数为对象类型提供者有个方法如下:
person对象
消费者使用:
a.从容器中获取一个bean,名称为上面配置的Id属性,强转为GenericServiceGenericService genericService = (GenericService) context.getBean("genericService");
b.调用genericService唯一的一个方法$invoke();对象类型的参数用Map封装,如果返回结果也是对象类型,也会被转成map
Map paramMap = new HashMap(); paramMap.put("class", "com.ned.entity.Person");
paramMap.put("name","啊哈"); paramMap.put("password","123456");
Map resultMap = (Map) genericService.$invoke("findPerson", new String[]{"com.ned.entity.Person"}, new Object[]{paramMap}); System.out.println(resultMap);
服务端泛化的实现服务接口不在提供者模块(也就是提供者是没有接口的情况,与泛化调用相反)
有个接口在消费者模块中(注意:在提供者模块是没有这个接口的)public interface ConsumerGenericApi {
String serviceGenericTest(String name);}
a.服务端实现GenericService接口:
public class ServiceGenericImpl implements GenericService {
public Object $invoke(String s, String[] strings, Object[] objects) throws GenericException { return "OK";
} }b.配置服务端提供者:
(com.ned.generic.ServiceGenericImpl,就是上面的实现类;interface="com.ned.consumer.generic.ConsumerGenericApi" 这是接口的全称,但不在提供这个的模块中)
c.配置消费者:(与正常的消费者一样)
(也就是接口在消费者,实现在提供者,因此serviceGenericTest方法调用,其实走了消费者的$invoke方法) ConsumerGenericApi consumerGenericApi = (ConsumerGenericApi) context.getBean("consumerGenericApi");
String t = consumerGenericApi.serviceGenericTest("t"); System.out.println(t);
15.分组聚合 merger="true" 布尔型,用在reference 或其的method上,true :把指定的组结果聚合,fasle:处哪个不聚合
/*注意的是,服务的 返回类型是有要求的,在com.alibaba.dubbo.rpc.cluster.merger这个包下的所有类型
也就是数组,list,map,set
14.多版本
消费者与提供者使用相同的version
13. 服务分组
使用场景:消费者的group,可以统一,从而实现开发/测试环境的快速切换调用不同的能力如:
消费者(如果所有提供者都配了group,消费者一定要添加group属性,可以是具体,可以是/*,如果提供者部分配置group,消费者有不配那么消费者只会使用不配group的提供的接口)
12.多协议在reference、service上通过protocol字段设置对于的protocol的名称即可(service上可以设置多个 如消费者指定某种协议 提供者提供多种协议(不同的服务提供不同的协议,或一个服务多个协议逗号隔开) 11.hessianx协议(使用短链接,适用于大文件传输,dubbo协议不适应于大文件传输的原因是使用长连接,) a.添加依赖 b.设置提供者协议 c.设置消费者协议(不用指定sever) 10. 静态服务应用部署后,服务是禁用状态,需要人工启动,断线后,也不会清除,要人工删除 标签 9.只注册,不订阅别人服务a.只是提供服务,不用注册中心订阅别的服务(不依赖别的服务); b.有两个注册中心R1与R2,服务S1在R1上注册了,不能在R2上注册,但是有应用A要使用服务S1并且应用的注册A的中心是由R1与R2集群的,这样会导致一些请求到R2上会没找到服务S1;因此应用A在配置注册中心集群时就必须对R2进行只注册(也就是所有依赖的服务都从R1中获取),但又不能把R2直接去掉,因为应用A要注册一下服务到R2上给别的应用使用; 8.只订阅,不注册自己服务 开发者要依赖使用通过注册中心上的服务,但是又不想把自己正在开发的服务注册到注册中心(原因正在开发的功能未完善,注册到注册中心可能会影响别调用未完善的服务)使用标签 7.直连提供者 三种方式:JVM参数、通过JVM参数指定配置文件、标签 如下图所示,消费者对于api.User这个服务是不通注册中心,而是直接连接消费者 配置文件的方式(用于多个接口要处理时,可以配置到单独的文件上):a.创建配置文件放到${user_home}/ dubbo-resolve.properties(注意不要改名) ${user.home}指的是 当前操作系统用户目录,如 Win7系统 Administrator的用户目录就是 C:UsersAdministrator b.编辑配置文件映射 安装上图还是会执行20889端口的原因是因为 jvm的配置会覆盖配置文件的配置,把-Dapi.User= dubbo://localhost:20889去除,就会直连20888标签reference上 标签笔记简单,直接在url中添加就可以,但是要注意的是看看会不会陪前面两种覆盖 6.线程模型(超链接文章详细说明)使用在protocal标签上 Dispatcher all 所有消息都派发到线程池,包括请求,响应,连接事件,断开事件,心跳等。(缺省)对应相关的类:com.alibaba.dubbo.remoting.transport.dispatcher.all.AllChannelHandler direct 所有消息都不派发到线程池,全部在 IO 线程上直接执行。对应的类:com.alibaba.dubbo.remoting.transport.dispatcher.direct.DirectDispatcher message 只有请求响应消息派发到线程池,其它连接断开事件,心跳等消息,直接在 IO 线程上执行。对应的类:com.alibaba.dubbo.remoting.transport.dispatcher.message.MessageOnlyDispatcher execution 只请求消息派发到线程池,不含响应,响应和其它连接断开事件,心跳等消息,直接在 IO 线程上执行。对应的类:com.alibaba.dubbo.remoting.transport.dispatcher.execution.ExecutionDispatcher connection 在 IO 线程上,将连接断开事件放入队列,有序逐个执行,其它消息派发到线程池。 对应的类:com.alibaba.dubbo.remoting.transport.dispatcher.connection.ConnectionOrderedDispatcher ThreadPool 5.负载均衡用于标签消费者method、提供者service、consumer、service(前面覆盖后面) 如: random(默认):按权重随机分配;roundrobin:轮循负载均衡;会出现错误累积问题 leastactive:活跃数指调用前后计数差(调用前的时刻减去响应后的时刻的值),可以说是最快响应的概率大一致hash: 4.集群方式 用于标识reference、service、consumer、provdier (前面覆盖后面)方式有: failover(默认):失败自动切换其他机器,配合retries=“1”(重试次数,默认3次) 可以用于非幂等的接口如添加类型的接口(重复调用可能会有问题,如添加类型接口)failfast:失败立即返回错误 failsafe:失败安全,出现异常时,直接忽略。(如超时:在消费者打印错误error级别日志,但不是报错,接口返回null,然后继续执行后面的代码 failback: 失败自动恢复,后台记录失败请求,定时重发。通常用于消息通知操作。(也是打印eror日志,返回null,但会一直重试,直到成功,默认5s周期)forking:并行调用多个服务器,只要一个成功即返回。通常用于实时性要求较高的读操作,但需要浪费更多服务资源 3.启动检查(启动消费者检查提供者、启动检查注册中心) 标签为check 默认是true(检查) 注意覆盖性jvm的覆盖方法的,方法的覆盖reference的,reference的覆盖consumer的启动消费者检查提供者: 可以用在consumer、reference、method、jvm启动参数( 用在 2.注解实现提供者和消费者提供者: a.在配置文件 扫描,更spring自动扫描一样道理 b.在接口实现类型面标注@service( 注意是dubbo不是spring的 ,@com.alibaba.dubbo.config.annotation.Service) 消费者:a.配置文件跟提供者一样 b.消费者用Reference注解,标准在引用的接口上(也就是提供者实现的接口,消费者要有的接口) 1.属性作用域(timeout等) a.方法的覆盖接口的,接口的覆盖 也就是Consumer的方法> Service的方法>Consumer标签的>Service标签的
)
)
)启动检查注册中心