Netty编解码相关的组件有以下⼏个:Channel、ChannelHandler、ChannelPipe等。
1.入站和出站
ChannelHandler⽤于处理⼊站和出站数据。
- ChannelHandler的实现类ChannelInboundHandlerAdapter表示⼊站程序,ChannelHandler的实现
- ChannelOutboundHandlerAdapter表示出站程序。
⼀个Channel包含⼀个ChannelPipeline,而ChannelPipeline维护着由多个ChannelHandlerContext组成的双向链表。且每个ChannelHandlerContext内包含⼀个ChannelHandler。
-
入站:无论是客户端还是服务端,对应着ChannelPipeline中链表的tail尾部。如果是接收数据,则为入站事件。即数据会从head到tail的过程,会依次经历多个ChannelHandler。
-
出站:如果发生的是发送数据的事件,则数据会从tail尾部发送到head头部,这个过程中会先后经历多个ChannelHandler。
入站和出站两种类型互不干扰
2.编解码器
⼊站和出站操作时,数据需要被编解码才能被正常处理。Netty提供了⼀些编解码器用于应对不同对象的编解码:
对象类型 | 编码器 | 解码器 |
---|---|---|
String | StringEncoder | StringDecoder |
Object | ObjectEncoder | ObjectDecoder |
protected void initChannel(SocketChannel socketChannel) throws Exception {
// 添加处理器: 处理器里面是真正处理业务的
ChannelPipeline pipeline = socketChannel.pipeline();
pipeline.addLast(new ObjectEncoder());
pipeline.addLast(new ObjectDecoder(Integer.MAX_VALUE, ClassResolvers.cacheDisabled(null)));
pipeline.addLast(new NettyServerHandler());
}
3.使用Protobuf提升序列化性能
为了提升性能,可以使⽤Protobuf或者Protostuff对数据进⾏序列化和反序列化,效率更⾼。
引入依赖
<!--protostuff-->
<dependency>
<groupId>com.dyuproject.protostuff</groupId>
<artifactId>protostuff-api</artifactId>
<version>1.0.10</version>
</dependency>
<dependency>
<groupId>com.dyuproject.protostuff</groupId>
<artifactId>protostuff-core</artifactId>
<version>1.0.10</version>
</dependency>
<dependency>
<groupId>com.dyuproject.protostuff</groupId>
<artifactId>protostuff-runtime</artifactId>
<version>1.0.11</version>
</dependency>
编写 ProtostuffUtil
public class ProtostuffUtil {
/**
* 序列化对象
*/
public static <T> byte[] serialize(T obj) {
if (obj == null) {
throw new RuntimeException("序列化对象(" + obj + ")!");
}
@SuppressWarnings("unchecked")
Schema<T> schema = (Schema<T>) RuntimeSchema.getSchema(obj.getClass());
LinkedBuffer buffer = LinkedBuffer.allocate(1024 * 1024);
byte[] protostuff = null;
try {
protostuff = ProtostuffIOUtil.toByteArray(obj, schema, buffer);
} catch (Exception e) {
throw new RuntimeException("序列化(" + obj.getClass() + ")对象(" + obj + ")发生异常!", e);
} finally {
buffer.clear();
}
return protostuff;
}
/**
* 反序列化对象
* @param paramArrayOfByte
* @param targetClass
* @return
*/
public static <T> T deserialize(byte[] paramArrayOfByte, Class<T> targetClass) {
if (paramArrayOfByte == null || paramArrayOfByte.length == 0) {
throw new RuntimeException("反序列化对象发生异常,byte序列为空!");
}
T instance = null;
try {
instance = targetClass.newInstance();
} catch (InstantiationException | IllegalAccessException e) {
throw new RuntimeException("反序列化过程中依据类型创建对象失败!", e);
}
Schema<T> schema = RuntimeSchema.getSchema(targetClass);
ProtostuffIOUtil.mergeFrom(paramArrayOfByte, instance, schema);
return instance;
}
/**
* 序列化集合对象
* @param objList
* @return
*/
public static <T> byte[] serializeList(List<T> objList) {
if (objList == null || objList.isEmpty()) {
throw new RuntimeException("序列化对象列表(" + objList + ")参数异常!");
}
@SuppressWarnings("unchecked")
Schema<T> schema = (Schema<T>) RuntimeSchema.getSchema(objList.get(0).getClass());
LinkedBuffer buffer = LinkedBuffer.allocate(1024 * 1024);
byte[] protostuff = null;
ByteArrayOutputStream bos = null;
try {
bos = new ByteArrayOutputStream();
ProtostuffIOUtil.writeListTo(bos, objList, schema, buffer);
protostuff = bos.toByteArray();
} catch (Exception e) {
throw new RuntimeException("序列化对象列表(" + objList + ")发生异常!", e);
} finally {
buffer.clear();
try {
if (bos != null) {
bos.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
return protostuff;
}
/**
* 反序列化集合对象
* @param paramArrayOfByte
* @param targetClass
* @return
*/
public static <T> List<T> deserializeList(byte[] paramArrayOfByte, Class<T> targetClass) {
if (paramArrayOfByte == null || paramArrayOfByte.length == 0) {
throw new RuntimeException("反序列化对象发生异常,byte序列为空!");
}
Schema<T> schema = RuntimeSchema.getSchema(targetClass);
List<T> result = null;
try {
result = ProtostuffIOUtil
.parseListFrom(new ByteArrayInputStream(paramArrayOfByte), schema);
} catch (IOException e) {
throw new RuntimeException("反序列化对象列表发生异常!", e);
}
return result;
}
}
使用 ProtostuffUtil
// 出站
public void channelActive(ChannelHandlerContext ctx) throws Exception {
ByteBuf byteBuf = Unpooled.copiedBuffer(ProtostuffUtil.serialize(new Student(1L, "zhangsan")));
ctx.writeAndFlush(byteBuf);
}
// 入站
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
ByteBuf buf = (ByteBuf) msg;
byte[] bytes = new byte[buf.readableBytes()];
buf.readBytes(bytes);
Student student = ProtostuffUtil.deserialize(bytes, Student.class);
System.out.println("客户端发送的数据:" + student);
}