序列化在Netty中的使用
- Java序列化的缺点
1、无法跨语言
对于Java序列化后的字节数组,别的语言无法进行反序列化
2、序列化后的码流过大
3、序列化性能低
- 使用JDK自带的序列化进行对象的传输
被传输的,实现了序列化接口的POJO
package org.zln.netty.five.part04.dto; import org.apache.commons.lang3.builder.ToStringBuilder; import java.io.Serializable; /** * 用户订购请求信息 * Created by sherry on 16/11/7. */ public class SubscribeReq implements Serializable{ //序列化 private static final long serialVersionUID = 1L; //订购编号 private int subReqID; //用户名 private String userName; //订购的产品名称 private String productName; //订购者联系电话 private String phoneName; //订购者家庭地址 private String address; @Override public String toString() { return new ToStringBuilder(this) .append("subReqID", subReqID) .append("userName", userName) .append("productName", productName) .append("phoneName", phoneName) .append("address", address) .toString(); } public int getSubReqID() { return subReqID; } public void setSubReqID(int subReqID) { this.subReqID = subReqID; } public String getUserName() { return userName; } public void setUserName(String userName) { this.userName = userName; } public String getProductName() { return productName; } public void setProductName(String productName) { this.productName = productName; } public String getPhoneName() { return phoneName; } public void setPhoneName(String phoneName) { this.phoneName = phoneName; } public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } }
package org.zln.netty.five.part04.dto; import org.apache.commons.lang3.builder.ToStringBuilder; import java.io.Serializable; /** * 订单请求的响应信息 * Created by sherry on 16/11/7. */ public class SubscribeResp implements Serializable { //序列化 private static final long serialVersionUID = 1L; //订购编号 private int subReqID; //订购结果 0-未成功 private int respCode; //可选的详细描述信息 private String desc; @Override public String toString() { return new ToStringBuilder(this) .append("subReqID", subReqID) .append("respCode", respCode) .append("desc", desc) .toString(); } public int getSubReqID() { return subReqID; } public void setSubReqID(int subReqID) { this.subReqID = subReqID; } public int getRespCode() { return respCode; } public void setRespCode(int respCode) { this.respCode = respCode; } public String getDesc() { return desc; } public void setDesc(String desc) { this.desc = desc; } }
服务端
package org.zln.netty.five.part04.server; import io.netty.bootstrap.ServerBootstrap; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelOption; import io.netty.channel.EventLoopGroup; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.nio.NioServerSocketChannel; import io.netty.handler.logging.LogLevel; import io.netty.handler.logging.LoggingHandler; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Created by sherry on 16/11/5. */ public class SubReqServer { /** * 服务端绑定端口号 */ private int PORT; public SubReqServer(int PORT) { this.PORT = PORT; } /** * 日志 */ private static Logger logger = LoggerFactory.getLogger(SubReqServer.class); public void bind() { /* NioEventLoopGroup是线程池组 包含了一组NIO线程,专门用于网络事件的处理 bossGroup:服务端,接收客户端连接 workGroup:进行SocketChannel的网络读写 */ EventLoopGroup bossGroup = new NioEventLoopGroup(); EventLoopGroup workGroup = new NioEventLoopGroup(); try { /* ServerBootstrap:用于启动NIO服务的辅助类,目的是降低服务端的开发复杂度 */ ServerBootstrap serverBootstrap = new ServerBootstrap(); serverBootstrap.group(bossGroup, workGroup) .channel(NioServerSocketChannel.class) .option(ChannelOption.SO_BACKLOG, 1024)//配置TCP参数,能够设置很多,这里就只设置了backlog=1024, .handler(new LoggingHandler(LogLevel.DEBUG)) .childHandler(new SubReqServerInitializer());//绑定I/O事件处理类 logger.debug("绑定端口号:" + PORT + ",等待同步成功"); /* bind:绑定端口 sync:同步阻塞方法,等待绑定完成,完成后返回 ChannelFuture ,主要用于通知回调 */ ChannelFuture channelFuture = serverBootstrap.bind(PORT).sync(); logger.debug("等待服务端监听窗口关闭"); /* closeFuture().sync():为了阻塞,服务端链路关闭后才退出.也是一个同步阻塞方法 */ channelFuture.channel().closeFuture().sync(); } catch (InterruptedException e) { logger.error(e.getMessage(), e); } finally { logger.debug("优雅退出,释放线程池资源"); bossGroup.shutdownGracefully(); workGroup.shutdownGracefully(); } } }
package org.zln.netty.five.part04.server; import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelPipeline; import io.netty.channel.socket.SocketChannel; import io.netty.handler.codec.serialization.ClassResolvers; import io.netty.handler.codec.serialization.ObjectDecoder; import io.netty.handler.codec.serialization.ObjectEncoder; /** * Created by sherry on 16/11/5. */ public class SubReqServerInitializer extends ChannelInitializer<SocketChannel> { @Override protected void initChannel(SocketChannel socketChannel) throws Exception { ChannelPipeline pipeline = socketChannel.pipeline(); /* ObjectDecoder:对实现了Serializable接口的POJO进行解码,用于解码请求对象 1024 * 1024:设置1M大小,为最大的单个对象的序列化后的字节长度 ClassResolvers.weakCachingConcurrentResolver(this.getClass().getClassLoader())):对类加载器进行缓存,线程安全.支持多线程并发访问,当虚拟机内存不足时,释放缓存时的内存,防止内存泄漏 */ pipeline.addLast(new ObjectDecoder(1024 * 1024, ClassResolvers.weakCachingConcurrentResolver(this.getClass().getClassLoader()))); /* ObjectEncoder:服务端在发送的时候,将对象进行编码操作 */ pipeline.addLast(new ObjectEncoder()); pipeline.addLast(new SubReqServerHandler()); } }
package org.zln.netty.five.part04.server; import io.netty.channel.ChannelHandlerAdapter; import io.netty.channel.ChannelHandlerContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.zln.netty.five.part03.SessionUtils; import org.zln.netty.five.part04.dto.SubscribeReq; import org.zln.netty.five.part04.dto.SubscribeResp; /** * Handler主要用于对网络事件进行读写操作,是真正的业务类 * 通常只需要关注 channelRead 和 exceptionCaught 方法 * Created by sherry on 16/11/5. */ public class SubReqServerHandler extends ChannelHandlerAdapter { /** * 日志 */ private Logger logger = LoggerFactory.getLogger(SubReqServerHandler.class); private static int count = 0; @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { SubscribeReq subscribeReq = (SubscribeReq) msg; logger.debug("收到的订单请求信息:\n"+subscribeReq); //根据请求信息,设置响应信息 SubscribeResp subscribeResp = getSubscribeResp(subscribeReq); ctx.writeAndFlush(subscribeResp); } private SubscribeResp getSubscribeResp(SubscribeReq subscribeReq) { SubscribeResp subscribeResp = new SubscribeResp(); subscribeResp.setSubReqID(subscribeReq.getSubReqID()); subscribeResp.setRespCode(subscribeReq.getSubReqID()); subscribeResp.setDesc("你订购的是一双红皮鞋,三天后发货"); return subscribeResp; } @Override public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { //将消息发送队列中的消息写入到SocketChannel中发送给对方 logger.debug("channelReadComplete"); ctx.flush(); } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { //发生异常时,关闭 ChannelHandlerContext,释放ChannelHandlerContext 相关的句柄等资源 logger.error(cause.getMessage(),cause); ctx.close(); } }
客户端
package org.zln.netty.five.part04.client; import io.netty.bootstrap.Bootstrap; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelOption; import io.netty.channel.EventLoopGroup; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.nio.NioSocketChannel; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.zln.netty.five.part03.EchoServer; /** * Created by sherry on 16/11/5. */ public class SubReqClient { /** * 日志 */ private Logger logger = LoggerFactory.getLogger(EchoServer.class); private String HOST; private int PORT; public SubReqClient(String HOST, int PORT) { this.HOST = HOST; this.PORT = PORT; } public void connect(){ //配置客户端NIO线程组 EventLoopGroup eventLoopGroup = new NioEventLoopGroup(); try { Bootstrap bootstrap = new Bootstrap(); bootstrap.group(eventLoopGroup) .channel(NioSocketChannel.class) .option(ChannelOption.TCP_NODELAY,true) .handler(new SubReqClientInitializer()); //发起异步连接操作 logger.debug("发起异步连接操作 - start"); ChannelFuture channelFuture = bootstrap.connect(HOST,PORT).sync(); logger.debug("发起异步连接操作 - end"); //等待客户端链路关闭 logger.debug("等待客户端链路关闭 - start"); channelFuture.channel().closeFuture().sync(); logger.debug("等待客户端链路关闭 - end"); } catch (InterruptedException e) { logger.error(e.getMessage(),e); }finally { //优雅的关闭 eventLoopGroup.shutdownGracefully(); } } }
package org.zln.netty.five.part04.client; import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelPipeline; import io.netty.channel.socket.SocketChannel; import io.netty.handler.codec.serialization.ClassResolvers; import io.netty.handler.codec.serialization.ObjectDecoder; import io.netty.handler.codec.serialization.ObjectEncoder; /** * Created by sherry on 16/11/5. */ public class SubReqClientInitializer extends ChannelInitializer<SocketChannel> { @Override protected void initChannel(SocketChannel socketChannel) throws Exception { ChannelPipeline pipeline = socketChannel.pipeline(); //与服务端不同,这里禁止对类加载器进行缓存 pipeline.addLast(new ObjectDecoder(1024, ClassResolvers.cacheDisabled(this.getClass().getClassLoader()))); pipeline.addLast(new ObjectEncoder()); pipeline.addLast(new SubReqClientHandler()); } }
package org.zln.netty.five.part04.client; import io.netty.channel.ChannelHandlerAdapter; import io.netty.channel.ChannelHandlerContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.zln.netty.five.part04.dto.SubscribeReq; import org.zln.netty.five.part04.dto.SubscribeResp; /** * Created by sherry on 16/11/5. */ public class SubReqClientHandler extends ChannelHandlerAdapter { /** * 日志 */ private Logger logger = LoggerFactory.getLogger(SubReqClientHandler.class); private static int count = 0; @Override public void channelActive(ChannelHandlerContext ctx) throws Exception { logger.debug("客户端连接上了服务端"); for (int i = 0; i < 10; i++) { SubscribeReq subscribeReq = getSubscribeReq(i); ctx.writeAndFlush(subscribeReq); } } private SubscribeReq getSubscribeReq(int i) { SubscribeReq subscribeReq = new SubscribeReq(); subscribeReq.setSubReqID(i); subscribeReq.setUserName("张柳宁"+i); subscribeReq.setProductName("红皮鞋"+i); subscribeReq.setPhoneName("123"+i); subscribeReq.setAddress("地址"+i); return subscribeReq; } @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { SubscribeResp subscribeResp = (SubscribeResp) msg; logger.debug("这是收到的第 " + (++count) + " 笔响应 -- " + subscribeResp); } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { ctx.close(); } }
- 使用Marshalling第三方jar进行序列化
POJO不变
工具类
package org.zln.netty.five.part05.marshalling; import io.netty.handler.codec.marshalling.DefaultMarshallerProvider; import io.netty.handler.codec.marshalling.DefaultUnmarshallerProvider; import io.netty.handler.codec.marshalling.MarshallerProvider; import io.netty.handler.codec.marshalling.MarshallingDecoder; import io.netty.handler.codec.marshalling.MarshallingEncoder; import io.netty.handler.codec.marshalling.UnmarshallerProvider; import org.jboss.marshalling.MarshallerFactory; import org.jboss.marshalling.Marshalling; import org.jboss.marshalling.MarshallingConfiguration; public class MarshallingCodeCFactory { public static MarshallingDecoder buildMarshallingDecoder() { /* * 通过 Marshalling 工具类的 getProvidedMarshallerFactory * 静态方法获取MarshallerFactory 实例, , 参数 serial 表示创建的是 Java 序列化工厂对象.它是由 * jboss-marshalling-serial 包提供 */ final MarshallerFactory marshallerFactory = Marshalling .getProvidedMarshallerFactory("serial"); /* * 创建 */ final MarshallingConfiguration configuration = new MarshallingConfiguration(); configuration.setVersion(5); UnmarshallerProvider provider = new DefaultUnmarshallerProvider( marshallerFactory, configuration); /* * provider : 提供商 maxSize : 单个对象最大尺寸 */ int maxSize = 1024 << 2; MarshallingDecoder decoder = new MarshallingDecoder(provider, maxSize); return decoder; } public static MarshallingEncoder buildMarshallingEncoder() { final MarshallerFactory marshallerFactory = Marshalling .getProvidedMarshallerFactory("serial"); final MarshallingConfiguration configuration = new MarshallingConfiguration(); configuration.setVersion(5); MarshallerProvider provider = new DefaultMarshallerProvider( marshallerFactory, configuration); MarshallingEncoder decoder = new MarshallingEncoder(provider); return decoder; } }
服务端
package org.zln.netty.five.part05.server; import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelPipeline; import io.netty.channel.socket.SocketChannel; import org.zln.netty.five.part05.marshalling.MarshallingCodeCFactory; /** * Created by sherry on 16/11/5. */ public class SubReqServerInitializer extends ChannelInitializer<SocketChannel> { @Override protected void initChannel(SocketChannel socketChannel) throws Exception { ChannelPipeline pipeline = socketChannel.pipeline(); //解码器 pipeline.addLast(MarshallingCodeCFactory.buildMarshallingDecoder()); //编码器 pipeline.addLast(MarshallingCodeCFactory.buildMarshallingEncoder()); pipeline.addLast(new SubReqServerHandler()); } }
客户端
package org.zln.netty.five.part05.client; import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelPipeline; import io.netty.channel.socket.SocketChannel; import org.zln.netty.five.part05.marshalling.MarshallingCodeCFactory; /** * Created by sherry on 16/11/5. */ public class SubReqClientInitializer extends ChannelInitializer<SocketChannel> { @Override protected void initChannel(SocketChannel socketChannel) throws Exception { ChannelPipeline pipeline = socketChannel.pipeline(); //解码器 pipeline.addLast(MarshallingCodeCFactory.buildMarshallingDecoder()); //编码器 pipeline.addLast(MarshallingCodeCFactory.buildMarshallingEncoder()); pipeline.addLast(new SubReqClientHandler()); } }
其他代码和使用JDK序列化的时候完全一样