在如今互聯網的架構趨勢下,微服務已經成爲一個不可或缺的服務架構了。將一個大的服務拆分若干子服務,然後遠程調用,已應對大流量、高併發的系統場景,如今開源的優秀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