RPC調用超時中斷機制的實現

在如今互聯網的架構趨勢下,微服務已經成爲一個不可或缺的服務架構了。將一個大的服務拆分若干子服務,然後遠程調用,已應對大流量、高併發的系統場景,如今開源的優秀RPC框架很多,例如 thrift、dubbo 、grpc等

本人公司也有兩套自主研發的RPC框架,通讀之後受益匪淺,下面分享一下,遠程調用第三方服務超時中斷機制的實現。在調用第三方服務時,如果服務提供方處理過於緩慢,會拖垮調用方,使調用方夯住,所以調用超時中斷機制很有必要,是保證服務的可用性的重要手段

典型的微服務項目,一次用戶請求,可能在後臺的調用流程會歷經多個服務,每個服務的可靠性是整個調用流程的前提

 

客戶端調用服務端流程:

 

 

本文不再過多的講解RPC調用流程,直接講解客戶端調用超時中斷的代碼實現。

原理也不復雜,利用ReentrantLock的Condition進行等待阻塞,等待相應的超時時間後,發現依然沒有收到服務端的響應結果後,判斷爲超時!

代碼實現:

首先定義一個netty客戶端,用於請求服務端,獲取返回結果

public class InvokerClient {

    private static Channel channel;

    public void init() throws Exception {
        Bootstrap bootstrap = new Bootstrap();
        bootstrap.group(new NioEventLoopGroup()).channel(NioSocketChannel.class)
                .option(ChannelOption.SO_KEEPALIVE, true)
                .handler(new ChannelInitializer<SocketChannel>() {
                    @Override
                    protected void initChannel(SocketChannel socketChannel) throws Exception {
                        // 處理來自服務端的返回結果
                        socketChannel.pipeline().addLast(new ReceiveHandle());
                    }
                });

        ChannelFuture cf = bootstrap.connect("127.0.0.1", 3344).sync();
        channel = cf.channel();
    }

    //請求服務端
    public Object call(Request request) {
        //此類是保證調用超時中斷的核心類
        RequestTask requestTask = new RequestTask();
        //將請求放入請求工廠,使用請求唯一標識seq,用於辨識服務端返回的對應的響應結果
        RequestFactory.put(request.getSeq(), requestTask);
        channel.writeAndFlush("hello");

        //此步是返回response,超時即中斷
        return requestTask.getResponse(request.getTimeOut());
    }
}

 

其中Request是請求參數,裏面有timeout超時時間,以及向服務端請求的參數
public class Request {

    private static final UUID uuid = UUID.randomUUID();

    private String seq = uuid.toString();

    private Object object;

    private long timeOut;

    public Object getObject() {
        return object;
    }

    public Request setObject(Object object) {
        this.object = object;
        return this;
    }

    public String getSeq() {
        return seq;
    }

    public long getTimeOut() {
        return timeOut;
    }

    public Request setTimeOut(long timeOut) {
        this.timeOut = timeOut;
        return this;
    }
}

核心的RequestTask類,用於接受服務端的返回結果,超時中斷

public class RequestTask {

    private boolean isDone = Boolean.FALSE;
    private ReentrantLock lock = new ReentrantLock();
    private Condition condition = lock.newCondition();

    Object response;

    //客戶端請求服務端後,立即調用此方法獲取返回結果,timeout爲超時時間
    public Object getResponse(long timeOut) {
        if (!isDone) {
            try {
                lock.lock();
                //此步等待timeout時間,阻塞,時間達到後,自動執行,此步是超時中斷的關鍵步驟
                if (condition.await(timeOut, TimeUnit.MILLISECONDS)) {
                    if (!isDone) {
                        return new TimeoutException();
                    }
                    return response;
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }
        return response;
    }

    public RequestTask setResponse(Object response) {
        lock.lock();
        try{
           //此步是客戶端收到服務端的響應結果後,寫入response
           this.response = response;
           //並喚起上面方法的阻塞狀態,此時阻塞結束,結果正常返回
           condition.signal();
           isDone = true;
        }finally{
           lock.unlock();
        }
        return this;
    }

    public boolean isDone() {
        return isDone;
    }

    public RequestTask setDone(boolean done) {
        isDone = done;
        return this;
    }

}
ReceiveHandle客戶端接收到服務端的響應結果處理handle
public class ReceiveHandle extends SimpleChannelInboundHandler {

    protected void channelRead0(ChannelHandlerContext channelHandlerContext, Object o) throws Exception {

        Response response = (Response) o;

        //通過seq從請求工廠找到請求的RequestTask
        RequestTask requestTask = RequestFactory.get(response.getSeq());
        
        //將響應結果寫入RequestTask
        requestTask.setResponse(response);
    }
}

RequestFactory請求工廠

public class RequestFactory {

    private static final Map<String, RequestTask> map = new ConcurrentHashMap<String, RequestTask>();

    public static void put(String uuid, RequestTask requestTask) {
        map.put(uuid, requestTask);
    }

    public static RequestTask get(String uuid) {
        return map.get(uuid);
    }
}

 

注: 本人利用業餘時間手寫了一套輕量級的rpc框架,裏面有用到
https://github.com/zhangta0/bigxiang

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章