RPC-dubbo基本使用

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:CallBackServiceImpl Exception 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 future = RpcContext.getContext().getFuture(); System.out.println(future.get()); 结果:(前两个null是异步立即返回null,第三个null是提供者getUserInfoByName2中获取隐形参数) ---- null null nullgetUserInfoByName2555(这个其实是2注释的远程调用的结果,同时RpcContext中第一次远程调用的结果已经配情况) ---- ======================================================================== 再调用: User2 user2 = (User2) context.getBean("user2"); RpcContext.getContext().setAttachment("test", "测试Rpcontext"); System.out.println(user2.getUserInfoByName("555")); Future future = RpcContext.getContext().getFuture();//两行顺序换了 System.out.println(user2.getUserInfoByName2("555")); System.out.println(future.get()); 结果: ---- null null 测试RpcontextgetUserInfoByName555(这一次可以获取到RPCContext中的隐性参数) ---- 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.参数为基本简单类型 消费者配置: a.给reference配置id; b.给reference配置interface,注意这个com.ned.generic.GenericApi在是接口的全称,但这接口在消费者是不存在的; c.给reference配置generic为true,标识泛化调用 使用: a.从容器中获取一个bean,名称为上面配置的Id属性,强转为GenericService GenericService 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属性,强转为GenericService GenericService 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.配置消费者: (与正常的消费者一样) d.消费者从容器中获取接口实例consumerGenericApi(c步骤中的id),直接调用方法: (也就是接口在消费者,实现在提供者,因此serviceGenericTest方法调用,其实走了消费者的$invoke方法) ConsumerGenericApi consumerGenericApi = (ConsumerGenericApi) context.getBean("consumerGenericApi"); String t = consumerGenericApi.serviceGenericTest("t"); System.out.println(t); 结果:(a步骤中return的:OK) ----- OK ----- 15.分组聚合 merger="true" 布尔型,用在reference 或其的method上,true :把指定的组结果聚合,fasle:处哪个不聚合 *注意的是,服务的返回类型是有要求的,在com.alibaba.dubbo.rpc.cluster.merger这个包下的所有类型 也就是数组,list,map,set 14.多版本 消费者与提供者使用相同的version 13. 服务分组 使用场景:消费者的group,可以统一,从而实现开发/测试环境的快速切换调用不同的能力如: service端,同一个接口的多种实现 消费者 (如果所有提供者都配了group,消费者一定要添加group属性,可以是具体,可以是*,如果提供者部分配置group,消费者有不配那么消费者只会使用不配group的提供的接口) 12.多协议 在reference、service上通过protocol字段设置对于的protocol的名称即可(service上可以设置多个   com.caucho   hessian   4.0.7   org.mortbay.jetty   jetty   6.1.26 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.只订阅,不注册自己服务 开发者要依赖使用通过注册中心上的服务,但是又不想把自己正在开发的服务注册到注册中心(原因正在开发的功能未完善,注册到注册中心可能会影响别调用未完善的服务) 使用标签 ,在注册标识reigistry设置为reigster="false" 就不会本服务注册注册中心了 7.直连提供者 三种方式:JVM参数、通过JVM参数指定配置文件、标签(前面的会覆盖后面的) jvm参数: 如下图所示,消费者对于api.User这个服务是不通注册中心,而是直接连接消费者 配置文件的方式(用于多个接口要处理时,可以配置到单独的文件上): a.创建配置文件放到${user_home}/ dubbo-resolve.properties(注意不要改名) ${user.home}指的是当前操作系统用户目录,如 Win7系统 Administrator的用户目录就是 C:\Users\Administrator b.编辑配置文件映射 安装上图还是会执行20889端口的原因是因为jvm的配置会覆盖配置文件的配置,把-Dapi.User=dubbo://localhost:20889去除,就会直连20888 标签reference上 标签笔记简单,直接在url中添加就可以,但是要注意的是看看会不会陪前面两种覆盖 6.线程模型(超链接文章详细说明)   使用在protocal标签上 Dispatcher all 所有消息都派发到线程池,包括请求,响应,连接事件,断开事件,心跳等。(缺省) 对应相关
50000+
5万行代码练就真实本领
17年
创办于2008年老牌培训机构
1000+
合作企业
98%
就业率

联系我们

电话咨询

0532-85025005

扫码添加微信