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了。
新闻热点
疑难解答