Demo3實現如下功能,客戶端發送一個Request對象(包含id,方法名,方法參數列表和調用參數列表)到服務端,服務端解析后調用客戶端指定的方法,并返回一個Response對象(包含id,是否異常,異常,返回值)。客戶端和服務端均基于netty實現。代碼已上傳到http://download.csdn.net/detail/mrbcy/9747693
這個真的很重要,具體的參考官方的guide吧,寫得通俗易懂。http://netty.io/wiki/user-guide-for-4.x.html#wiki-h3-9
對象的編碼和解碼基本使用了Demo1中寫的PRotoStuffUtil工具類,它可以將一個對象轉換成byte[],或者從一個byte[]轉換成一個對象。但是這里還有一個問題,由于tcp協議的特性,對方多次發送的數據我們可能一次性就收到了,或者發送了一次,但是我們分多次收到。為了解決這個問題,我在netty的編碼部分做了處理,在對象的數據之前加了一個字節來存放對象數據的長度。解碼部分也做了相應的處理,先讀取數據的長度,然后等到有足夠的數據再來讀取對象。關鍵代碼如下:
編碼部分:
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception { if(clazz.isInstance(msg)){ byte[] data = ProtostuffUtil.serializer(msg); ByteBuf encoded = ctx.alloc().buffer(data.length+4); encoded.writeInt(data.length); encoded.writeBytes(data); ctx.write(encoded, promise); }}解碼部分:
@Overrideprotected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception { // 前4字節記錄長度 if (in.readableBytes() < 4) { return; } in.markReaderIndex(); int dataLength = in.readInt(); if (dataLength < 0) { // 出錯了 ctx.close(); } if (in.readableBytes() < dataLength) { in.resetReaderIndex(); return; } //將ByteBuf轉換為byte[] byte[] data = new byte[dataLength]; in.readBytes(data); //將data轉換成object @SuppressWarnings("unchecked") Object obj = ProtostuffUtil.deserializer(data, clazz); out.add(obj);}這里涉及了反射的知識。服務端拿到客戶端傳入的request對象后,根據客戶端指定的方法和參數來調用,然后把結果通過response發送給客戶端。關鍵代碼如下:
RpcResponse response = new RpcResponse();try { RpcRequest request = (RpcRequest) msg; System.out.println("服務器收到調用請求:" + request); response.setId(request.getId()); Method method = SampleServiceImpl.class.getDeclaredMethod(request.getMethodName(), request.getParamTypes()); Object result = method.invoke(serviceImpl, request.getArgs()); response.setResult(result);} catch (Exception e) { response.setSuccess(false); response.setError(e);}寫到這一步,框架的核心功能已經實現了一大部分,接下來就是用Spring了。
新聞熱點
疑難解答