在上一小节中,使用原生的block io来进行网络传输。本节使用效率更高的noblock io来代替之,netty是优秀的异步网络框架。
netty的网络库思想极为优秀,c++的muduo网络库的源码正是借鉴了netty的网络模型。如下图,一个典型的多reactor模型,每一个线程都是一个事件循环,主线程负责监听文件描述符,当有请求来临的收,把请求分发给其他的子反应堆,子反应堆负责对请求的处理,从socket的接受缓冲区里把数据往用户接受缓冲区里面搬运,直到把socket接受缓冲区里的数据办完为止。然后就是对接受缓冲区里的数据进行解码,比方说一条http请求。然后做计算,这个计算可以用线程池去做,做完之后把数据往发送缓冲区里面送,然后调用send方法。send方法也很简单,用epoll监听socket发送缓冲区是否有剩余空间,如果有就把用户的发送缓冲区的数据往socket的发送缓冲区里面送,直到填满socket的发送缓冲区,获取已经把一条记录完整的从用户的发送缓冲区送入了socket的发送缓冲区。用原生的epoll实现远比使用netty要负责,netty的底层肯定是非常负责的,但是在使用上是很友好的。
netty的相关知识在我的一篇博客中提过,有兴趣可以区阅读那篇博客,有netty简单的使用。把rpcclient和rpcserver抽象成通用接口:
public interface RpcClient { Object sendRequest(RpcRequest rpcRequest); }public interface RpcServer { void start(int port); }
nettyserver的实现:
public class NettyServer implements RpcServer { private static final Logger logger = LoggerFactory.getLogger(NettyServer.class); @Override public void start(int port) { EventLoopGroup bossGroup = new NioEventLoopGroup(); EventLoopGroup workerGroup = new NioEventLoopGroup(); try { ServerBootstrap serverBootstrap = new ServerBootstrap(); serverBootstrap.group(bossGroup, workerGroup) .channel(NioServerSocketChannel.class) .handler(new LoggingHandler(LogLevel.INFO)) .option(ChannelOption.SO_BACKLOG, 256) .option(ChannelOption.SO_KEEPALIVE, true) .childOption(ChannelOption.TCP_NODELAY, true) .childHandler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel ch) throws Exception { ChannelPipeline pipeline = ch.pipeline(); pipeline.addLast(new CommonEncoder(new JsonSerializer())); pipeline.addLast(new CommonDecoder()); pipeline.addLast(new NettyServerHandler()); } }); ChannelFuture future = serverBootstrap.bind(port).sync(); future.channel().closeFuture().sync(); } catch (InterruptedException e) { logger.error("启动服务器时有错误发生: ", e); } finally { bossGroup.shutdownGracefully(); workerGroup.shutdownGracefully(); } } }
数据入站的时候会调用CommonDecoder()进行数据解码,数据出站的时候调用CommonEncoder(new JsonSerializer())对数据进行编码,NettyServerHandler起到数据处理的作用。
客户端的实现:
public class NettyClient implements RpcClient { private static final Logger logger = LoggerFactory.getLogger(NettyClient.class); private String host; private int port; private static final Bootstrap bootstrap; public NettyClient(String host, int port) { this.host = host; this.port = port; } static { logger.info("1111111111111111"); EventLoopGroup group = new NioEventLoopGroup(); bootstrap = new Bootstrap(); bootstrap.group(group) .channel(NioSocketChannel.class) .option(ChannelOption.SO_KEEPALIVE, true) .handler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel ch) throws Exception { ChannelPipeline pipeline = ch.pipeline(); pipeline.addLast(new CommonDecoder()) .addLast(new CommonEncoder(new JsonSerializer())) .addLast(new NettyClientHandler()); } }); } @Override public Object sendRequest(RpcRequest rpcRequest) { try { ChannelFuture future = bootstrap.connect(host, port).sync(); logger.info("客户端连接到服务器 {}:{}", host, port); System.out.println("使用了编码器2"); Channel channel = future.channel(); if(channel != null) { channel.writeAndFlush(rpcRequest).addListener(future1 -> { if(future1.isSuccess()) { logger.info(String.format("客户端发送消息: %s", rpcRequest.toString())); } else { logger.error("发送消息时有错误发生: ", future1.cause()); } }); channel.closeFuture().sync(); AttributeKey<RpcResponse> key = AttributeKey.valueOf("rpcResponse"); RpcResponse rpcResponse = channel.attr(key).get(); return rpcResponse.getData(); } } catch (InterruptedException e) { logger.error("发送消息时有错误发生: ", e); } return null; } }
在静态代码块中配置好netty客户端,加上编解码的handler,处理数据的handler:NettyClientHandler.使用sendRequest发送rpcrequest对象后,数据流经过编码器,然后发送。发送是非阻塞的,立即返回,通过以下代码阻塞的获取数据。NettyClientHandler会把服务器发送的数据经过解码后放入channel的attr中等待将来获取结果。
channel.closeFuture().sync(); AttributeKey<RpcResponse> key = AttributeKey.valueOf("rpcResponse"); RpcResponse rpcResponse = channel.attr(key).get(); return rpcResponse.getData();
自定义协议和编解码器:
魔数:检验此包是否为错包
Package Type:表明包是request包还是response包
Serializer Type:使用什么样的序列化器
Data Length:数据长度
Data Bytes:具体携带的数据
CommonEncoder集成了MessageToByteEncoder,其主要功能是把要发送的对象包装成协议包,使用json作为序列化和反序列化:
public class CommonEncoder extends MessageToByteEncoder { private static final int MAGIC_NUMBER = 0xCAFEBABE; private final CommonSerializer serializer; public CommonEncoder(CommonSerializer serializer) { this.serializer = serializer; } @Override protected void encode(ChannelHandlerContext ctx, Object msg, ByteBuf out) throws Exception { //msg是要进行编码的对象,out是发送缓冲区 out.writeInt(MAGIC_NUMBER); if(msg instanceof RpcRequest) { out.writeInt(PackageType.REQUEST_PACK.getCode()); } else { out.writeInt(PackageType.RESPONSE_PACK.getCode()); } out.writeInt(serializer.getCode()); byte[] bytes = serializer.serialize(msg); out.writeInt(bytes.length); out.writeBytes(bytes); } }
CommonDecoder负责把包解码为对应的消息对象:一般步骤就是先读取魔数,再读取包的类型,然后确定用的是哪一种的序列化方式,读取消息的长度,然后反序列化把消息还原成对象。
public class CommonDecoder extends ReplayingDecoder { private static final Logger logger = LoggerFactory.getLogger(CommonDecoder.class); private static final int MAGIC_NUMBER = 0xCAFEBABE; @Override protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception { int magic = in.readInt(); if(magic != MAGIC_NUMBER) { logger.error("不识别的协议包: {}", magic); throw new RpcException(RpcError.UNKNOWN_PROTOCOL); } int packageCode = in.readInt(); Class<?> packageClass; if(packageCode == PackageType.REQUEST_PACK.getCode()) { packageClass = RpcRequest.class; } else if(packageCode == PackageType.RESPONSE_PACK.getCode()) { packageClass = RpcResponse.class; } else { logger.error("不识别的数据包: {}", packageCode); throw new RpcException(RpcError.UNKNOWN_PACKAGE_TYPE); } int serializerCode = in.readInt(); System.out.println("serializerCode:" + serializerCode); CommonSerializer serializer = CommonSerializer.getByCode(serializerCode); if(serializer == null) { logger.error("不识别的反序列化器: {}", serializerCode); throw new RpcException(RpcError.UNKNOWN_SERIALIZER); } int length = in.readInt(); byte[] bytes = new byte[length]; in.readBytes(bytes); Object obj = serializer.deserialize(bytes, packageClass); out.add(obj); } }
序列化接口,一共四个方法:序列化,反序列化,获得该序列化器的编号,已经根据编号获取序列化器.
public interface CommonSerializer { byte[] serialize(Object obj); Object deserialize(byte[] bytes, Class<?> clazz); int getCode(); static CommonSerializer getByCode(int code) { switch (code) { case 1: return new JsonSerializer(); default: return null; } } }
实现了json序列化器,在 RpcRequest 反序列化时,由于其中有一个字段是 Object 数组,在反序列化时序列化器会根据字段类型进行反序列化,而 Object 就是一个十分模糊的类型,会出现反序列化失败的现象,这时就需要 RpcRequest 中的另一个字段 ParamTypes 来获取到 Object 数组中的每个实例的实际类,辅助反序列化,这就是 handleRequest() 方法的作用。
public class JsonSerializer implements CommonSerializer { private static final Logger logger = LoggerFactory.getLogger(JsonSerializer.class); private ObjectMapper objectMapper = new ObjectMapper(); @Override public byte[] serialize(Object obj) { try { return objectMapper.writeValueAsBytes(obj); } catch (JsonProcessingException e) { logger.error("序列化时有错误发生: {}", e.getMessage()); e.printStackTrace(); return null; } } @Override public Object deserialize(byte[] bytes, Class<?> clazz) { try { Object obj = objectMapper.readValue(bytes, clazz); if(obj instanceof RpcRequest) { obj = handleRequest(obj); } return obj; } catch (IOException e) { logger.error("反序列化时有错误发生: {}", e.getMessage()); e.printStackTrace(); return null; } } /* 这里由于使用JSON序列化和反序列化Object数组,无法保证反序列化后仍然为原实例类型 需要重新判断处理 */ private Object handleRequest(Object obj) throws IOException { RpcRequest rpcRequest = (RpcRequest) obj; for(int i = 0; i < rpcRequest.getParamTypes().length; i ++) { Class<?> clazz = rpcRequest.getParamTypes()[i]; if(!clazz.isAssignableFrom(rpcRequest.getParameters()[i].getClass())) { byte[] bytes = objectMapper.writeValueAsBytes(rpcRequest.getParameters()[i]); rpcRequest.getParameters()[i] = objectMapper.readValue(bytes, clazz); } } return rpcRequest; } @Override public int getCode() { return SerializerCode.valueOf("JSON").getCode(); } }
NettyServerHandler和NettyClientHandler负责处理数据。NettyServerHandler负责接受RpcRquest对象,执行本地服务调用,然后发送给客户端,发送的动作会触发出站处理器,即encoder。
public class NettyServerHandler extends SimpleChannelInboundHandler<RpcRequest> { private static final Logger logger = LoggerFactory.getLogger(NettyServerHandler.class); private static RequestHandler requestHandler; private static ServiceRegistry serviceRegistry; static { requestHandler = new RequestHandler(); serviceRegistry = new DefaultServiceRegistry(); } @Override protected void channelRead0(ChannelHandlerContext ctx, RpcRequest msg) throws Exception { try { logger.info("服务器接收到请求: {}", msg); String interfaceName = msg.getInterfaceName(); Object service = serviceRegistry.getService(interfaceName); Object result = requestHandler.handle(msg, service); ChannelFuture future = ctx.writeAndFlush(RpcResponse.success(result)); future.addListener(ChannelFutureListener.CLOSE); } finally { ReferenceCountUtil.release(msg); } } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { logger.error("处理过程调用时有错误发生:"); cause.printStackTrace(); ctx.close(); } }
NettyClientHandler是一个InboundHandler,对RpcResponse对象感兴趣,接受服务端返回的信息,并且用保存在channel的attr属性当,供以后客户端异步获取结果。
public class NettyClientHandler extends SimpleChannelInboundHandler<RpcResponse> { private static final Logger logger = LoggerFactory.getLogger(NettyClientHandler.class); @Override protected void channelRead0(ChannelHandlerContext ctx, RpcResponse msg) throws Exception { try { logger.info(String.format("客户端接收到消息: %s", msg)); AttributeKey<RpcResponse> key = AttributeKey.valueOf("rpcResponse"); ctx.channel().attr(key).set(msg); ctx.channel().close(); } finally { ReferenceCountUtil.release(msg); } } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { logger.error("过程调用时有错误发生:"); cause.printStackTrace(); ctx.close(); } }