consumer的DubboResponseTimeoutScanTimer线程
考虑这样一种情况,由于网络延时,consumer先抛出超时异常,一段时间后又收到了已经超时的响应,dubbo是怎么处理的?
拆分为3步看:
1. consumer的DubboResponseTimeoutScanTimer进行扫描
DubboResponseTimeoutScanTimer负责扫描响应,如果发现超时,自行构造一个超时响应,并处理。
Future,Request,Response共用同一个id

//DefaultFuture内部类
private static class RemotingInvocationTimeoutScan implements Runnable { public void run() {
while (true) {
try {
for (DefaultFuture future : FUTURES.values()) {
if (future == null || future.isDone()) {
continue;
}
if (System.currentTimeMillis() - future.getStartTimestamp() > future.getTimeout()) {
// consumer创建一个超时响应
// create exception response.
Response timeoutResponse = new Response(future.getId());
// set timeout status.
timeoutResponse.setStatus(future.isSent() ? Response.SERVER_TIMEOUT : Response.CLIENT_TIMEOUT);
timeoutResponse.setErrorMessage(future.getTimeoutMessage(true));
// handle response.
DefaultFuture.received(future.getChannel(), timeoutResponse);
}
}
Thread.sleep(30);
} catch (Throwable e) {
logger.error("Exception when scan the timeout invocation of remoting.", e);
}
}
}
} //DefaultFuture类
public static void received(Channel channel, Response response) {
try {
//首先删除future对象
DefaultFuture future = FUTURES.remove(response.getId());
if (future != null) {
future.doReceived(response);
} else {
logger.warn("The timeout response finally returned at "
+ (new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date()))
+ ", response " + response
+ (channel == null ? "" : ", channel: " + channel.getLocalAddress()
+ " -> " + channel.getRemoteAddress()));
}
} finally {
CHANNELS.remove(response.getId());
}
} private void doReceived(Response res) {
lock.lock();
try {
response = res;
if (done != null) {
//唤醒等待的线程(也许有,也许没有)
done.signal();
}
} finally {
lock.unlock();
}
if (callback != null) {
invokeCallback(callback);
}
}
2. consumer因为超时抛异常
//DefaultFuture
public Object get(int timeout) throws RemotingException {
if (timeout <= 0) {
timeout = Constants.DEFAULT_TIMEOUT;
}
if (! isDone()) {
long start = System.currentTimeMillis();
lock.lock();
try {
while (! isDone()) {
// 被DubboResponseTimeoutScanTimer线程唤醒,但是有个超时的响应,所以isDone返回true
done.await(timeout, TimeUnit.MILLISECONDS);
if (isDone() || System.currentTimeMillis() - start > timeout) {
break;
}
}
} catch (InterruptedException e) {
throw new RuntimeException(e);
} finally {
lock.unlock();
}
if (! isDone()) {
throw new TimeoutException(sent > 0, channel, getTimeoutMessage(false));
}
}
// isDone返回true,进入returnFromResponse
return returnFromResponse();
} private Object returnFromResponse() throws RemotingException {
Response res = response;
if (res == null) {
throw new IllegalStateException("response cannot be null");
}
if (res.getStatus() == Response.OK) {
return res.getResult();
}
if (res.getStatus() == Response.CLIENT_TIMEOUT || res.getStatus() == Response.SERVER_TIMEOUT) {
//此处抛异常
throw new TimeoutException(res.getStatus() == Response.SERVER_TIMEOUT, channel, res.getErrorMessage());
}
throw new RemotingException(channel, res.getErrorMessage());
}
3. 迟到的请求到来时
//DefaultFuture
public static void received(Channel channel, Response response) {
try {
//DubboResponseTimeoutScanTimer已经删除了迟到的请求
//所以走else分支
DefaultFuture future = FUTURES.remove(response.getId());
if (future != null) {
future.doReceived(response);
} else {
logger.warn("The timeout response finally returned at "
+ (new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date()))
+ ", response " + response
+ (channel == null ? "" : ", channel: " + channel.getLocalAddress()
+ " -> " + channel.getRemoteAddress()));
}
} finally {
CHANNELS.remove(response.getId());
}
}
静态分析代码时,以为先是步骤2,然后是步骤1,但实际调试时,结果是步骤1,步骤2.
consumer的DubboResponseTimeoutScanTimer线程的更多相关文章
- consumer的DubboClientHandler线程池
1. 创建线程池 创建线程池的调用栈如下: SimpleDataStore把线程池存放在map中. public class NettyClient extends AbstractClient { ...
- java 线程 Thread 使用介绍,包含wait(),notifyAll() 等函数使用介绍
(原创,转载请说明出处!谢谢--http://www.cnblogs.com/linguanh/) 此文目的为了帮助大家较全面.通俗地了解线程 Thread 相关基础知识! 目录: --线程的创建: ...
- 【原创】kafka consumer源代码分析
顾名思义,就是kafka的consumer api包. 一.ConsumerConfig.scala Kafka consumer的配置类,除了一些默认值常量及验证参数的方法之外,就是consumer ...
- Java 线程间通讯(管道流方式)
一.管道流是JAVA中线程通讯的常用方式之一,基本流程如下: 1)创建管道输出流PipedOutputStream pos和管道输入流PipedInputStream pis 2)将pos和pis匹配 ...
- Java 线程间通讯(共享变量方式)
Java线程间通讯,最常用的方式便是共享变量方式,多个线程共享一个静态变量就可以实现在线程间通讯,但是这需要注意的就是线程同步问题. 一.没考虑线程同步: package com.wyf; publi ...
- java线程(2)-线程间通信
方法一 通过访问共享变量的方式(注:需要处理同步问题) 方法二 通过管道流 其中方法一有两种实现方法,即 方法一a)通过内部类实现线程的共享变量 public class Innersharethr ...
- 设计Kafka的High Level Consumer
原文:https://cwiki.apache.org/confluence/display/KAFKA/Consumer+Group+Example 为什么使用High Level Consumer ...
- 读Kafka Consumer源码
最近一直在关注阿里的一个开源项目:OpenMessaging OpenMessaging, which includes the establishment of industry guideline ...
- JAVA线程与线程、进程与进程间通信
I.线程与线程间通信 一.基本概念以及线程与进程之间的区别联系: 关于进程和线程,首先从定义上理解就有所不同1.进程是什么?是具有一定独立功能的程序.它是系统进行资源分配和调度的一个独立单位,重点在系 ...
随机推荐
- django 项目中使用多数据库 multiple databases
假如在一个django项目中使用到了不只一个数据库, 其实这在大一点的工程中很常见,比如主从库 那么会涉及到如下一些东西 1, 定义 在settings中的DATABASE中定义会使用到的数据,比如除 ...
- WIFI模块对比介绍
一.ESP8266(官网 https://espressif.com/)1 简介 乐鑫智能互联平台——ESCP 拥有高性能无线SOC,给移动平台设计师带来福音,它 以最低成本提供最大实用性,为WiFi ...
- jquery插件--问题类(新增&&删除)简易版
HTML: <!doctype html> <head> <meta charset="utf-8" /> <script src=&qu ...
- numpy.random.randn()与rand()的区别【转】
本文转载自:https://blog.csdn.net/u010758410/article/details/71799142 numpy中有一些常用的用来产生随机数的函数,randn()和rand( ...
- P3216 [HNOI2011]数学作业 (矩阵快速幂)
P3216 [HNOI2011]数学作业 题目描述 小 C 数学成绩优异,于是老师给小 C 留了一道非常难的数学作业题: 给定正整数 NN 和 MM ,要求计算 Concatenate (1 .. N ...
- P3386 【模板】二分图匹配 -网络流版
二分图匹配 题目背景 二分图 感谢@一扶苏一 提供的hack数据 题目描述 给定一个二分图,结点个数分别为n,m,边数为e,求二分图最大匹配数 输入输出格式 输入格式: 第一行,n,m,e 第二至e+ ...
- 【基础配置】Dubbo的配置及使用
1. Dubbo是什么? Dubbo是一个分布式服务框架,致力于提供高性能和透明化的RPC远程服务调用方案,以及SOA服务治理方案.简单的说,dubbo就是个服务框架,如果没有分布式的需求,其实是不需 ...
- Codeforces Beta Round #16 div 2 C.Monitor最大公约数
C. Monitor time limit per test 0.5 second memory limit per test 64 megabytes input standard input ou ...
- C++学习笔记(二)——交换函数(swap)
这次我们要透过一个简单的函数swap深入理解函数传参的本质以及在C++中如何选择传参方式. 先来看第一段程序: void swap(int x, int y) { int temp = y; y = ...
- RPC 服务器不可用
1,查看“Remote Procedure Call (RPC)”启动2,设置下面选项.・Hyper-V服务器->虚拟交换机管理器,在虚拟交换机的[连接类型]下, 勾选[允许管理操作系统共享此网 ...