https://blog.csdn.net/u011296165/article/details/90199145
總覽
Dubbo在調用服務的時候使用了DefaultFuture這個類,其中有一個概念是異步調用轉成同步調用。核心思想就是管程。而實現方式就是使用lock和condition。
condition 是java 併發包中的一個類, 在java內置管程中是一個條件變量的,而condition是可以實現一個管程多個條件變量。 lock是java的重入鎖。
關於管程和condtion可以參考:https://blog.csdn.net/u011296165/article/details/89944540
關於lock重入鎖可以參考 : https://blog.csdn.net/u011296165/article/details/89873493
java 併發包是使用lock和condition實現的管程。
關於同步異步
在調用方法的時候,調用方是否需要等待返回結果才能執行下一步。如果需要等待就是同步,不需要等待就是異步。
關於java異步方式實現有兩種方式
調用方創建一個線程,這種調用我們稱爲異步調用。
實現方法的時候開啓一個線程去執行主要邏輯 ,主線程直接return,這種方法我們稱爲異步方法。
dubbo源碼
以下是dubbo的源碼,我自己跟了一點點代碼,並將代碼註釋,一部分引入了別的累的東西,我將代碼截圖粘到下面
/**
* DefaultFuture.
*/
public class DefaultFuture implements ResponseFuture {
private static final Logger logger = LoggerFactory.getLogger(DefaultFuture.class);
/**
* @Author 田培融
* @Description 管程去調用遠程服務
* @Date 18:31 2019/5/15
* @Param
* @return
**/
private static final Map<Long, Channel> CHANNELS = new ConcurrentHashMap<>();
/**
* @Author 田培融
* @Description 多個線程去調用返回不同的結果
* @Date 18:31 2019/5/15
* @Param
* @return
**/
private static final Map<Long, DefaultFuture> FUTURES = new ConcurrentHashMap<>();
// 超時時間
public static final Timer TIME_OUT_TIMER = new HashedWheelTimer(
new NamedThreadFactory("dubbo-future-timeout", true),
30,
TimeUnit.MILLISECONDS);
// invoke id.
private final long id;
// dubbo 自定義的管道
private final Channel channel;
// dubbo 自定義的請求對象
private final Request request;
// 超時時間
private final int timeout;
// java併發包中的重入鎖(管程中的入口隊列和看門人)
private final Lock lock = new ReentrantLock();
// java併發包中的條件變量(管程中的條件變量隊列)
private final Condition done = lock.newCondition();
// 開始時間
private final long start = System.currentTimeMillis();
// 使用volatile關鍵字去修飾以達到多線程內存可見
private volatile long sent;
// dubbo 自定義的響應體
private volatile Response response;
// dubbo中自定義的響應體回調接口
private volatile ResponseCallback callback;
/**
* @Author 田培融
* @Description 構造方法,需要傳
* @Date 18:38 2019/5/15
* @Param [channel, request, timeout]
* @return
**/
private DefaultFuture(Channel channel, Request request, int timeout) {
this.channel = channel;
this.request = request;
this.id = request.getId();
this.timeout = timeout > 0 ? timeout : channel.getUrl().getPositiveParameter(TIMEOUT_KEY, DEFAULT_TIMEOUT);
// put into waiting map.
FUTURES.put(id, this);
CHANNELS.put(id, channel);
}
/**
* check time out of the future
*/
private static void timeoutCheck(DefaultFuture future) {
TimeoutCheckTask task = new TimeoutCheckTask(future);
TIME_OUT_TIMER.newTimeout(task, future.getTimeout(), TimeUnit.MILLISECONDS);
}
/**
* init a DefaultFuture
* 1.init a DefaultFuture
* 2.timeout check
*
* @param channel channel
* @param request the request
* @param timeout timeout
* @return a new DefaultFuture
*/
public static DefaultFuture newFuture(Channel channel, Request request, int timeout) {
final DefaultFuture future = new DefaultFuture(channel, request, timeout);
// timeout check
timeoutCheck(future);
return future;
}
/**
* @Author 田培融
* @Description 在futuresMap中獲取
* @Date 18:57 2019/5/15
* @Param [id]
* @return org.apache.dubbo.remoting.exchange.support.DefaultFuture
**/
public static DefaultFuture getFuture(long id) {
return FUTURES.get(id);
}
/**
* @Author 田培融
* @Description 判斷channel是否在管道map中
* @Date 18:58 2019/5/15
* @Param [channel]
* @return boolean
**/
public static boolean hasFuture(Channel channel) {
return CHANNELS.containsValue(channel);
}
/**
* @Author 田培融
* @Description 發送請求
* 1. 先在futures隊列中獲取futures 在調用 dosent方法將sent值設置爲當前系統時間
* @Date 18:59 2019/5/15
* @Param [channel, request]
* @return void
**/
public static void sent(Channel channel, Request request) {
DefaultFuture future = FUTURES.get(request.getId());
if (future != null) {
future.doSent();
}
}
/**
* close a channel when a channel is inactive
* directly return the unfinished requests.
* 關閉管程
*
* @param channel channel to close
*/
public static void closeChannel(Channel channel) {
for (Map.Entry<Long, Channel> entry : CHANNELS.entrySet()) {
if (channel.equals(entry.getValue())) {
DefaultFuture future = getFuture(entry.getKey());
if (future != null && !future.isDone()) {
Response disconnectResponse = new Response(future.getId());
disconnectResponse.setStatus(Response.CHANNEL_INACTIVE);
disconnectResponse.setErrorMessage("Channel " +
channel +
" is inactive. Directly return the unFinished request : " +
future.getRequest());
DefaultFuture.received(channel, disconnectResponse);
}
}
}
}
/**
* @Author 田培融
* @Description 在響應中取值 如果就調用doReceived釋放掉在條件對列中等待的線程
* @Date 19:09 2019/5/15
* @Param [channel, response]
* @return void
**/
public static void received(Channel channel, Response response) {
try {
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());
}
}
@Override
public Object get() throws RemotingException {
return get(timeout);
}
/**
* @Author 田培融
* @Description 線程通過這個方法去獲取響應
* @Date 19:22 2019/5/15
* @Param [timeout]
* @return java.lang.Object
**/
@Override
public Object get(int timeout) throws RemotingException {
if (timeout <= 0) {
timeout = DEFAULT_TIMEOUT;
}
if (!isDone()) {
long start = System.currentTimeMillis();
lock.lock();
try {
while (!isDone()) {
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));
}
}
return returnFromResponse();
}
public void cancel() {
Response errorResult = new Response(id);
errorResult.setErrorMessage("request future has been canceled.");
response = errorResult;
FUTURES.remove(id);
CHANNELS.remove(id);
}
@Override
public boolean isDone() {
return response != null;
}
/**
* @Author 田培融
* @Description 線程通過這個方法去將直接獲取到的響應結果放到這個類中的成員變量,並去喚醒在條件隊列中等待的線程
* @Date 19:25 2019/5/15
* @Param [callback]
* @return void
**/
@Override
public void setCallback(ResponseCallback callback) {
if (isDone()) {
invokeCallback(callback);
} else {
boolean isdone = false;
lock.lock();
try {
if (!isDone()) {
this.callback = callback;
} else {
isdone = true;
}
} finally {
lock.unlock();
}
if (isdone) {
invokeCallback(callback);
}
}
}
private static class TimeoutCheckTask implements TimerTask {
private DefaultFuture future;
TimeoutCheckTask(DefaultFuture future) {
this.future = future;
}
@Override
public void run(Timeout timeout) {
if (future == null || future.isDone()) {
return;
}
// 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);
}
}
/**
* @Author 田培融
* @Description 直正去執行獲取到的返回值放到response中
* @Date 19:25 2019/5/15
* @Param [c]
* @return void
**/
private void invokeCallback(ResponseCallback c) {
ResponseCallback callbackCopy = c;
if (callbackCopy == null) {
throw new NullPointerException("callback cannot be null.");
}
Response res = response;
if (res == null) {
throw new IllegalStateException("response cannot be null. url:" + channel.getUrl());
}
if (res.getStatus() == Response.OK) {
try {
callbackCopy.done(res.getResult());
} catch (Exception e) {
logger.error("callback invoke error .result:" + res.getResult() + ",url:" + channel.getUrl(), e);
}
} else if (res.getStatus() == Response.CLIENT_TIMEOUT || res.getStatus() == Response.SERVER_TIMEOUT) {
try {
TimeoutException te = new TimeoutException(res.getStatus() == Response.SERVER_TIMEOUT, channel, res.getErrorMessage());
callbackCopy.caught(te);
} catch (Exception e) {
logger.error("callback invoke error ,url:" + channel.getUrl(), e);
}
} else {
try {
RuntimeException re = new RuntimeException(res.getErrorMessage());
callbackCopy.caught(re);
} catch (Exception e) {
logger.error("callback invoke error ,url:" + channel.getUrl(), e);
}
}
}
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());
}
private long getId() {
return id;
}
private Channel getChannel() {
return channel;
}
private boolean isSent() {
return sent > 0;
}
public Request getRequest() {
return request;
}
private int getTimeout() {
return timeout;
}
private long getStartTimestamp() {
return start;
}
private void doSent() {
sent = System.currentTimeMillis();
}
/**
* @Author 田培融
* @Description 實現管程的一部分,直釋放到等待的線程
* @Date 19:29 2019/5/15
* @Param [res]
* @return void
**/
private void doReceived(Response res) {
lock.lock();
try {
response = res;
done.signalAll();
} finally {
lock.unlock();
}
if (callback != null) {
invokeCallback(callback);
}
}
private String getTimeoutMessage(boolean scan) {
long nowTimestamp = System.currentTimeMillis();
return (sent > 0 ? "Waiting server-side response timeout" : "Sending request timeout in client-side")
+ (scan ? " by scan timer" : "") + ". start time: "
+ (new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date(start))) + ", end time: "
+ (new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date())) + ","
+ (sent > 0 ? " client elapsed: " + (sent - start)
+ " ms, server elapsed: " + (nowTimestamp - sent)
: " elapsed: " + (nowTimestamp - start)) + " ms, timeout: "
+ timeout + " ms, request: " + request + ", channel: " + channel.getLocalAddress()
+ " -> " + channel.getRemoteAddress();
}
}
獲取request中的id
將id值 初始化進入Response中
現在將代碼需求重新說一下,就是線程T1在調用 Rpc之後就將線程阻塞住,當返回結果後就在將T1線程喚醒。這裏使用的是管程,。因爲業務牽扯過多,爲了節省時間就把關於管程的代碼就節取了出來 。
// 創建鎖與條件變量
private final Lock lock
= new ReentrantLock();
private final Condition done
= lock.newCondition();
private
private volatile Response response;
// 調用方通過該方法等待結果
Object get(int timeout){
long start = System.nanoTime();
lock.lock();
try {
while (!isDone()) {
done.await(timeout);
long cur=System.nanoTime();
if (isDone() ||
cur-start > timeout){
break;
}
}
} finally {
lock.unlock();
}
if (!isDone()) {
throw new TimeoutException();
}
return returnFromResponse();
}
// RPC 結果是否已經返回
boolean isDone() {
return response != null;
}
// RPC 結果返回時調用該方法
private void doReceived(Response res) {
lock.lock();
try {
response = res;
if (done != null) {
done.signal();
}
} finally {
lock.unlock();
}
}
代碼流程分析
線程T1在調用 get()方法後調用 lock鎖進入管程,使用while()循環進入等待。使用finally調用釋放鎖。 當rpc返回結果後在調用doReceived()方法進行附值。並能知條件變量中等待的線程。
這裏在對成員變量的修飾符進行解釋
假如T2線程爲rpc線程,T2線程會接收一個t1線程new出的對象。並將對象放到自己的線程棧內存中,因此lock需要使用final修飾(final 如果是引用類型的變量,則在對其初始化之後便不能再讓其指向另一個對象。)Lock就是一個引用類型的變量這樣t2線程中的lock變量就是指向的t1線程中new出的Lock對象。
response是用volatile修飾的,當t2線程獲取值並對response進行操作後,response由於volatile修改,因此t1線程也可以讀取到。這裏是使用volatile修改的變量保證了內存可見性,就是說volatile修改的變量,線程都會去堆內存中去直接操作,而不是將變量拉到棧空間中操作。這樣所有的線程都是操作的同一個變量。
————————————