dubbo事件通知机制 (2)

此文已由作者赵计刚授权网易云社区发布。

欢迎访问网易云社区,了解更多网易技术产品运营经验。


142      * 反射执行xxxService.onthrow方法:至少要有一个入参且第一个入参类型为Throwable或其子类,接收返回结果
143      */
144     private void fireThrowCallback(final Invoker<?> invoker, final Invocation invocation, final Throwable exception) {
145         final Method onthrowMethod = (Method) StaticContext.getSystemContext().get(StaticContext.getKey(invoker.getUrl(), invocation.getMethodName(), Constants.ON_THROW_METHOD_KEY));
146         final Object onthrowInst = StaticContext.getSystemContext().get(StaticContext.getKey(invoker.getUrl(), invocation.getMethodName(), Constants.ON_THROW_INSTANCE_KEY));
147 
148         //onthrow callback not configured
149         if (onthrowMethod == null && onthrowInst == null) {
150             return;
151         }
152         if (onthrowMethod == null || onthrowInst == null) {
153             throw new IllegalStateException("service:" + invoker.getUrl().getServiceKey() + " has a onthrow callback config , but no such " + (onthrowMethod == null ? "method" : "instance") + " found. url:" + invoker.getUrl());
154         }
155         if (onthrowMethod != null && !onthrowMethod.isAccessible()) {
156             onthrowMethod.setAccessible(true);
157         }
158         Class<?>[] rParaTypes = onthrowMethod.getParameterTypes();
159         if (rParaTypes[0].isAssignableFrom(exception.getClass())) {
160             try {
161                 Object[] args = invocation.getArguments();
162                 Object[] params;
163 
164                 if (rParaTypes.length > 1) {
165                     // onthrow(xx, Object[]) 两个参数:第一个参数接收exception,第二个接收所有的真实请求参数
166                     if (rParaTypes.length == 2 && rParaTypes[1].isAssignableFrom(Object[].class)) {
167                         params = new Object[2];
168                         params[0] = exception;
169                         params[1] = args;
170                     // onthrow(xx, Object... args) 多个参数:第一个参数接收exception,后边几个接收所有的真实请求参数
171                     } else {
172                         params = new Object[args.length + 1];
173                         params[0] = exception;
174                         System.arraycopy(args, 0, params, 1, args.length);
175                     }
176                 } else {
177                     // onthrow(xx) 只有一个参数:接收exception
178                     params = new Object[]{exception};
179                 }
180                 onthrowMethod.invoke(onthrowInst, params);
181             } catch (Throwable e) {
182                 logger.error(invocation.getMethodName() + ".call back method invoke error . callback method :" + onthrowMethod + ", url:" + invoker.getUrl(), e);
183             }
184         } else {
185             logger.error(invocation.getMethodName() + ".call back method invoke error . callback method :" + onthrowMethod + ", url:" + invoker.getUrl(), exception);
186         }
187     }
188 }

从@Activate(group = Constants.CONSUMER)来看FutureFilter只用在consumer端;不管是同步调用还是异步调用,都会走FutureFilter。

原理:

  • 首先走oninvoke(String name)方法;

  • 然后走sayHello(String name)

  • 最后根据同步还是异步分别走不同的逻辑。 

其中同步很简单,看sayHello(String name)的返回结果RpcResult中是否有exception对象,如果有,执行onthrow(Throwable ex, String name);如果没有执行onreturnWithoutParam(String result)。

异步的操作:由于不知道provider什么时候回执行完毕,所以要添加回调等待provider端返回结果后,再执行onthrow(Throwable ex, String name)或者onreturnWithoutParam(String result),这种模式很重要,这是统计异步方法调用时间的一种非常好的模式。

重点看一下异步!

 

三、异步回调模式

 1     private void asyncCallback(final Invoker<?> invoker, final Invocation invocation) {
 2         Future<?> f = RpcContext.getContext().getFuture();
 3         if (f instanceof FutureAdapter) {
 4             ResponseFuture future = ((FutureAdapter<?>) f).getFuture();
 5             // 3.1 调用服务之后:设置回调ResponseCallback对象到DefaultFuture中,当provider返回响应时,执行DefaultFuture.doReceived方法,该方法会调用ResponseCallback对象的done或者caught方法
 6             future.setCallback(new ResponseCallback() {
 7                 public void done(Object rpcResult) {
 8                     if (rpcResult == null) {
 9                         logger.error(new IllegalStateException("invalid result value : null, expected " + Result.class.getName()));
10                         return;
11                     }
12                     ///must be rpcResult
13                     if (!(rpcResult instanceof Result)) {
14                         logger.error(new IllegalStateException("invalid result type :" + rpcResult.getClass() + ", expected " + Result.class.getName()));
15                         return;
16                     }
17                     Result result = (Result) rpcResult;
18                     if (result.hasException()) {
19                         fireThrowCallback(invoker, invocation, result.getException());
20                     } else {
21                         fireReturnCallback(invoker, invocation, result.getValue());
22                     }
23                 }
24 
25                 public void caught(Throwable exception) {
26                     fireThrowCallback(invoker, invocation, exception);
27                 }
28             });
29         }
30     }


上述的future对象是DefaultFuture,这里首先new了一个ResponseCallback回调函数,设置到了DefaultFuture的ResponseCallback callback属性中。来看一下DefaultFuture类:


 1     private volatile Response response;
 2     private volatile ResponseCallback callback;
 3 
 4     public boolean isDone() {
 5         return response != null;
 6     }
 7 
 8     public void setCallback(ResponseCallback callback) {
 9         if (isDone()) {
10             invokeCallback(callback);
11         } else {
12             boolean isdone = false;
13             lock.lock();
14             try {
15                 if (!isDone()) {
16                     this.callback = callback;
17                 } else {
18                     isdone = true;
19                 }
20             } finally {
21                 lock.unlock();
22             }
23             if (isdone) {
24                 invokeCallback(callback);
25             }
26         }
27     }


 1     private void invokeCallback(ResponseCallback c) {
 2         ResponseCallback callbackCopy = c;
 3         if (callbackCopy == null) {
 4             throw new NullPointerException("callback cannot be null.");
 5         }
 6         c = null;
 7         Response res = response;
 8         if (res == null) {
 9             throw new IllegalStateException("response cannot be null. url:" + channel.getUrl());
10         }
11 
12         if (res.getStatus() == Response.OK) {
13             try {
14                 // 返回正常,回调ResponseCallback回调函数的done方法
15                 callbackCopy.done(res.getResult());
16             } catch (Exception e) {
17                 logger.error("callback invoke error .reasult:" + res.getResult() + ",url:" + channel.getUrl(), e);
18             }
19         } else if (res.getStatus() == Response.CLIENT_TIMEOUT || res.getStatus() == Response.SERVER_TIMEOUT) {
20             try {
21                 TimeoutException te = new TimeoutException(res.getStatus() == Response.SERVER_TIMEOUT, channel, res.getErrorMessage());
22                 // 如果超时,回调ResponseCallback回调函数的caught方法
23                 callbackCopy.caught(te);
24             } catch (Exception e) {
25                 logger.error("callback invoke error ,url:" + channel.getUrl(), e);
26             }
27         } else {
28             try {
29                 RuntimeException re = new RuntimeException(res.getErrorMessage());
30                 // 其他异常,回调ResponseCallback回调函数的caught方法
31                 callbackCopy.caught(re);
32             } catch (Exception e) {
33                 logger.error("callback invoke error ,url:" + channel.getUrl(), e);
34             }
35         }
36     }


从setCallback(ResponseCallback callback),如果此时provider端已经返回了响应(response!=null),则直接执行ResponseCallback回调函数中的done方法或者caught方法;否则,将上边创建的ResponseCallback实例赋值给DefaultFuture的ResponseCallback callback属性中。那么之后会在什么时候执行回调函数的方法呢?当consumer接收到provider的响应的时候!


 1     public static void received(Channel channel, Response response) {
 2         try {
 3             DefaultFuture future = FUTURES.remove(response.getId());
 4             if (future != null) {
 5                 future.doReceived(response);
 6             } else {
 7                 logger.warn("The timeout response finally returned at "
 8                         + (new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date()))
 9                         + ", response " + response
10                         + (channel == null ? "" : ", channel: " + channel.getLocalAddress()
11                         + " -> " + channel.getRemoteAddress()));
12             }
13         } finally {
14             CHANNELS.remove(response.getId());
15         }
16     }
17 
18     private void doReceived(Response res) {
19         lock.lock();
20         try {
21             response = res;
22             if (done != null) {
23                 done.signal();
24             }
25         } finally {
26             lock.unlock();
27         }
28         // 调用回调函数
29         if (callback != null) {
30             invokeCallback(callback);
31         }
32     }


当provider返回响应时,会调用DefaultFuture.received(Channel channel, Response response)方法(9.3 客户端接收响应信息(异步转同步的实现)),此时会执行回调函数。事件通知的源码就分析完了!最后看一个回调模式的使用场景:统计异步方法的调用时间。


 1     private void asyncCallback(final Invoker<?> invoker, final Invocation invocation) {
 2         Future<?> f = RpcContext.getContext().getFuture();
 3         final long start = System.currentTimeMillis();
 4         if (f instanceof FutureAdapter) {
 5             ResponseFuture future = ((FutureAdapter<?>) f).getFuture();
 6             future.setCallback(new ResponseCallback() {
 7                 public void done(Object rpcResult) {
 8                     long cost = System.currentTimeMillis() - start;
 9                 }
10             });
11         }
12     }


上边的代码只是一个形式,实际上start时间需要在调用sayHello方法之前进行记录。


免费体验云安全(易盾)内容安全、验证码等服务


更多网易技术、产品、运营经验分享请点击


相关文章:
【推荐】 人力资源管理中的大数据应用之道
【推荐】 Android输入法弹出时覆盖输入框问题

### Dubbo 分布式服务框架的原理与工作机制 Dubbo 是一个高性能、轻量级的开源 Java RPC 框架,旨在提供高效的远程服务调用解决方案,并支持自动化的服务注册与发现功能[^1]。它的设计目标是帮助开发者快速构建分布式服务体系,同时在大规模微服务环境中提供强大的治理能力。 #### 1. Dubbo 的架构概述 Dubbo 的整体架构由多个核心组件构成,这些组件共同协作以完成服务的发布、调用以及管理。以下是其主要组成部分及其作用: - **Provider**: 提供者节点负责暴露服务接口并将其注册到注册中心。 - **Consumer**: 消费者节点通过向注册中心查询来获取可用的服务列表,并发起远程调用请求。 - **Registry**: 注册中心用于存储服务地址信息,充当 Provider 和 Consumer 之间的桥梁。 - **Monitor**: 监控中心记录服务调用次数和耗时等指标数据,便于后续分析和服务优化。 - **Container**: 容器环境承载着整个 Dubbo 应用程序运行所需的资源和支持。 这种分层的设计使得 Dubbo 可以灵活应对不同规模的应用场景需求[^3]。 #### 2. RPC 调用流程详解 RPC (Remote Procedure Call) 即远程过程调用,是一种让客户端像调用本地方法一样访问远端服务器上的某个函数的技术[^2]。具体而言,在 Dubbo 中实现了一个完整的 RPC 流程如下所示: 1. 当消费者想要调用某项服务时,它会先从配置文件或者动态加载的方式得知该服务对应的 Stub 接口定义; 2. 随后利用代理模式创建出针对此接口的一个实例对象——即所谓的 Client Proxy; 3. 在实际执行过程中,当用户操作这个代理对象的时候,底层便会触发一系列逻辑处理动作,包括但不限于参数序列化打包成字节流形式发送给指定的目标机器; 4. 对方接收到消息之后再经过反序列化解析还原原始输入值交给真正的业务处理器去响应请求最后把结果返回回去同样经历类似的转换步骤直至最终呈现给最初发出指令的一方为止。 下面是简化版伪代码表示这一交互行为: ```java // Service Interface Definition public interface HelloService { String sayHello(String name); } // On the consumer side, create a proxy instance of the service. HelloService helloService = ReferenceConfig.getProxy(HelloService.class); // Invoke remote method as if it were local call. String result = helloService.sayHello("World"); System.out.println(result); // Output: Hello World! ``` 在此期间涉及到的关键技术点还有诸如如何选择合适的连接池大小?怎样设置超时时间防止长时间等待影响用户体验等问题都需要仔细考量才能达到最佳效果[^2]。 #### 3. 核心机制剖析 为了更好地理解 Dubbo 工作机制背后隐藏的秘密,下面重点介绍几个重要方面: ##### (1)通信协议支持 Dubbo 支持多种不同的传输协议,默认采用的是 dubbo:// 方案,这是一种基于 TCP 的私有二进制协议,相比起 HTTP 来说效率更高因为少了大量冗余头部信息开销;当然也允许切换至其他选项比如 hessian:// 或 restful api 形式的 json-rpc 等满足特定场合下的特殊要求。 ##### (2)序列化方式多样性 考虑到跨平台兼容性问题以及性能因素考虑,Dubbo 内置了几种主流的数据编码格式可供挑选例如 fastjson、protobuf 等以便于适应各类复杂度各异的任务负载情况之下均能找到平衡点达成理想状态。 ##### (3)负载均衡算法 为了让流量均匀分布减少单点压力提高系统稳定性,Dubbo 实现了一系列内置策略其中包括随机法(Random LoadBalance),轮询(RoundRobinLoadBalance),最少活跃数(LeastActiveLoadBalance)等等可根据实际情况自由组合运用从而获得最优解路径规划方案。 ##### (4)容错处理机制 面对不可避免的各种异常状况发生可能性,提前做好预案显得尤为重要因此 Dubbo 设计了一套完善的错误恢复体系涵盖重试(Failover Cluster), 忽略失败(Failsafe Cluster), 广播通知(Broadcast Cluster)等多种类型确保即使部分节点出现问题也不会导致全局瘫痪局面出现维持正常运转秩序不变样[^3]。 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值