一.Netty序列化介绍
序列化管理操作:Java原生实现(性能比较差)、JSON(Restful)、MessagePack、Marshalling、AVRO、....
netty本身直接支持有原生的 Java序列化操作,直接配置已有的程序类即可
MessagePack:类似于JSON,但是要比JSON传输的更加小巧同时速度也快。它定义一个自己的压缩算法,例如:boolean只有true和false,但是有了MP就可以通过0和1描述了;
Marshalling:使用JBoss实现的序列化处理操作,是基于传统的Java序列化形式的一种升级。
JSON:是一种标准做法,但是JSON需要清楚的问题是传输体积要大,但是传输的信息明确,本次使用FastJSON操作。
二.实例
1.公共netty类
Member.java
package com.bijian.vo;
import java.io.Serializable;
public class Member implements Serializable {
private String mid ;
private String name ;
private Integer age ;
private Double salary ;
public Member() {}
public Member(String mid,String name,Integer age,Double salary) {
this.mid = mid ;
this.name = name ;
this.age = age ;
this.salary = salary ;
}
public String getMid() {
return mid;
}
public void setMid(String mid) {
this.mid = mid;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public Double getSalary() {
return salary;
}
public void setSalary(Double salary) {
this.salary = salary;
}
@Override
public String toString() {
return "Member{" +
"mid='" + mid + '\'' +
", name='" + name + '\'' +
", age=" + age +
", salary=" + salary +
'}';
}
}
EchoServerHandler.java
package com.bijian.http.server.server.handler;
import com.bijian.vo.Member;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.util.ReferenceCountUtil;
/**
* 处理Echo的操作方式,其中ChannelInboundHandlerAdapter是针对于数据输入的处理
* Netty是基于NIO的一种开发框架的封装,这里面和AIO是没有任何关系的。
*/
public class EchoServerHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
try {
System.out.println(msg.getClass() + " **************");
Member member = (Member) msg ;
System.err.println("{服务器}" + member);
member.setName("【ECHO】" + member.getName());
ctx.writeAndFlush(member); // 回应的输出操作
} finally {
ReferenceCountUtil.release(msg) ; // 释放缓存
}
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
ctx.close() ;
}
}
EchoClientHandler.java
package com.bijian.http.server.client.handler;
import com.bijian.vo.Member;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.util.ReferenceCountUtil;
/**
* 需要进行数据的读取操作,服务器端处理完成的数据信息会进行读取
*/
public class EchoClientHandler extends ChannelInboundHandlerAdapter {
private static final int REPEAT = 500;// 消息重复发送次数
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
Member member = new Member(); // 现在直接进行对象的发送
member.setMid("xiaoli");
member.setName("小李老师");
member.setAge(16);
member.setSalary(1.1);
ctx.writeAndFlush(member) ; // 直接进行对象实例发送
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
// 只要服务器端发送完成信息之后,都会执行此方法进行内容的输出操作
try {
Member member = (Member) msg ;
System.out.println(member); // 输出服务器端的响应内容
} finally {
ReferenceCountUtil.release(msg); // 释放缓存
}
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
ctx.close();
}
}
EchoServerMain.java
package com.bijian.http.server.server.main;
import com.bijian.http.server.server.EchoServer;
public class EchoServerMain {
public static void main(String[] args) throws Exception {
new EchoServer().run();
}
}
EchoClientMain.java
package com.bijian.http.server.client.main;
import com.bijian.http.server.client.EchoClient;
public class EchoClientMain {
public static void main(String[] args) throws Exception {
new EchoClient().run();
}
}
2.使用Java原生的序列化程序管理实现对象网络传输
项目结构如下:
EchoServer.java
package com.bijian.http.server.server;
import com.bijian.http.server.server.handler.EchoServerHandler;
import com.bijian.info.HostInfo;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import io.netty.handler.codec.LengthFieldPrepender;
import io.netty.handler.codec.serialization.ClassResolvers;
import io.netty.handler.codec.serialization.ObjectDecoder;
import io.netty.handler.codec.serialization.ObjectEncoder;
/**
* 实现了基础的线程池与网络连接的配置项
*/
public class EchoServer {
public void run() throws Exception { // 进行服务器端的启动处理
// 线程池是提升服务器性能的重要技术手段,利用定长的线程池可以保证核心线程的有效数量
// 在Netty之中线程池的实现分为两类:主线程池(接收客户端连接)、工作线程池(处理客户端连接)
EventLoopGroup bossGroup = new NioEventLoopGroup(10); // 创建接收线程池
EventLoopGroup workerGroup = new NioEventLoopGroup(20); // 创建工作线程池
System.out.println("服务器启动成功,监听端口为:" + HostInfo.PORT);
try {
// 创建一个服务器端的程序类进行NIO启动,同时可以设置Channel
ServerBootstrap serverBootstrap = new ServerBootstrap(); // 服务器端
// 设置要使用的线程池以及当前的Channel类型
serverBootstrap.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class);
// 接收到信息之后需要进行处理,于是定义子处理器
serverBootstrap.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
socketChannel.pipeline().addLast(new ObjectDecoder(ClassResolvers.cacheDisabled((this.getClass().getClassLoader())))) ;
socketChannel.pipeline().addLast(new ObjectEncoder()) ;
socketChannel.pipeline().addLast(new EchoServerHandler()); // 追加了处理器
}
});
// 可以直接利用常亮进行TCP协议的相关配置
serverBootstrap.option(ChannelOption.SO_BACKLOG, 128);
serverBootstrap.childOption(ChannelOption.SO_KEEPALIVE, true);
// ChannelFuture描述的时异步回调的处理操作
ChannelFuture future = serverBootstrap.bind(HostInfo.PORT).sync();
future.channel().closeFuture().sync();// 等待Socket被关闭
} finally {
workerGroup.shutdownGracefully() ;
bossGroup.shutdownGracefully() ;
}
}
}
EchoClient.java
package com.bijian.http.server.client;
import com.bijian.http.server.client.handler.EchoClientHandler;
import com.bijian.info.HostInfo;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import io.netty.handler.codec.LengthFieldPrepender;
import io.netty.handler.codec.serialization.ClassResolvers;
import io.netty.handler.codec.serialization.ObjectDecoder;
import io.netty.handler.codec.serialization.ObjectEncoder;
public class EchoClient {
public void run() throws Exception {
// 1、如果现在客户端不同,那么也可以不使用多线程模式来处理;
// 在Netty中考虑到代码的统一性,也允许你在客户端设置线程池
EventLoopGroup group = new NioEventLoopGroup(); // 创建一个线程池
try {
Bootstrap client = new Bootstrap(); // 创建客户端处理程序
client.group(group).channel(NioSocketChannel.class)
.option(ChannelOption.TCP_NODELAY, true) // 允许接收大块的返回数据
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
socketChannel.pipeline().addLast(new ObjectDecoder(ClassResolvers.cacheDisabled((this.getClass().getClassLoader())))) ;
socketChannel.pipeline().addLast(new ObjectEncoder()) ;
socketChannel.pipeline().addLast(new EchoClientHandler()); // 追加了处理器
}
});
ChannelFuture channelFuture = client.connect(HostInfo.HOST_NAME, HostInfo.PORT).sync();
channelFuture.channel().closeFuture().sync(); // 关闭连接
} finally {
group.shutdownGracefully();
}
}
}
先启动EchoServerMain.java,再启动EchoClientMain.java,运行效果如下所示:
3.MessagePack实现传输序列化
项目结构如下所示:
pom.xml中引入msgpack
<dependency>
<groupId>org.msgpack</groupId>
<artifactId>msgpack</artifactId>
</dependency>
Member.java增加@Message注解
package com.bijian.vo;
import org.msgpack.annotation.Message;
import java.io.Serializable;
@Message
public class Member implements Serializable {
private String mid ;
private String name ;
private Integer age ;
private Double salary ;
public Member() {}
public Member(String mid,String name,Integer age,Double salary) {
this.mid = mid ;
this.name = name ;
this.age = age ;
this.salary = salary ;
}
public String getMid() {
return mid;
}
public void setMid(String mid) {
this.mid = mid;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public Double getSalary() {
return salary;
}
public void setSalary(Double salary) {
this.salary = salary;
}
@Override
public String toString() {
return "Member{" +
"mid='" + mid + '\'' +
", name='" + name + '\'' +
", age=" + age +
", salary=" + salary +
'}';
}
}
测试类MessagePackDemoA.java
package com.bijian.msgpack;
import com.bijian.vo.Member;
import org.msgpack.MessagePack;
import org.msgpack.template.Templates;
import java.util.ArrayList;
import java.util.List;
public class MessagePackDemoA {
public static void main(String[] args) throws Exception {
List<Member> allMembers = new ArrayList<Member>() ;
for(int x = 0 ; x < 10 ; x ++) {
Member member = new Member() ;
member.setMid("MLDN - " + x);
member.setName("Hello - " + x);
member.setAge(10);
member.setSalary(1.1);
allMembers.add(member) ;
}
MessagePack msgPack = new MessagePack() ;
byte data [] = msgPack.write(allMembers) ;
System.out.println(data.length);
{ // 将数据解析回来
List<Member> all = msgPack.read(data, Templates.tList(msgPack.lookup(Member.class))) ;
System.out.println(all);
}
}
}
运行测试类结果如下:
MessagePackDecoder.java
package com.bijian.netty.serious;
import com.bijian.vo.Member;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToMessageDecoder;
import org.msgpack.MessagePack;
import java.util.List;
public class MessagePackDecoder extends MessageToMessageDecoder<ByteBuf> {
@Override
protected void decode(ChannelHandlerContext channelHandlerContext, ByteBuf msg, List<Object> list) throws Exception {
int len = msg.readableBytes(); // 获取读取的数据长度
byte[] data = new byte[len]; // 准备读取数据的空间
msg.getBytes(msg.readerIndex(), data, 0, len); // 读取数据
MessagePack msgPack = new MessagePack() ;
System.out.println(msgPack.read(data));
// list.add(msgPack.read(data)) ;
list.add(msgPack.read(data,msgPack.lookup(Member.class))) ;
}
}
MessagePackEncoder.java
package com.bijian.netty.serious;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToByteEncoder;
import org.msgpack.MessagePack;
public class MessagePackEncoder extends MessageToByteEncoder<Object> {
@Override
protected void encode(ChannelHandlerContext channelHandlerContext, Object msg, ByteBuf byteBuf) throws Exception {
MessagePack msgPack = new MessagePack() ;
byte [] raw = msgPack.write(msg) ; // 进行对象的编码操作
byteBuf.writeBytes(raw) ;
}
}
EchoServerHandler.java
package com.bijian.netty.server.handler
import com.bijian.vo.Member;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.util.ReferenceCountUtil;
/**
* 处理Echo的操作方式,其中ChannelInboundHandlerAdapter是针对于数据输入的处理
* Netty是基于NIO的一种开发框架的封装,这里面和AIO是没有任何关系的。
*/
public class EchoServerHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
try {
System.out.println(msg.getClass() + " **************");
Member member = (Member) msg ;
System.err.println("{服务器}" + member);
member.setName("【ECHO】" + member.getName());
ctx.writeAndFlush(member); // 回应的输出操作
} finally {
ReferenceCountUtil.release(msg) ; // 释放缓存
}
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
ctx.close() ;
}
}
EchoServer.java
package com.bijian.http.server.server;
import com.bijian.http.server.server.handler.EchoServerHandler;
import com.bijian.info.HostInfo;
import com.bijian.netty.serious.MessagePackDecoder;
import com.bijian.netty.serious.MessagePackEncoder;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import io.netty.handler.codec.LengthFieldPrepender;
import io.netty.handler.codec.serialization.ClassResolvers;
import io.netty.handler.codec.serialization.ObjectDecoder;
import io.netty.handler.codec.serialization.ObjectEncoder;
/**
* 实现了基础的线程池与网络连接的配置项
*/
public class EchoServer {
public void run() throws Exception { // 进行服务器端的启动处理
// 线程池是提升服务器性能的重要技术手段,利用定长的线程池可以保证核心线程的有效数量
// 在Netty之中线程池的实现分为两类:主线程池(接收客户端连接)、工作线程池(处理客户端连接)
EventLoopGroup bossGroup = new NioEventLoopGroup(10); // 创建接收线程池
EventLoopGroup workerGroup = new NioEventLoopGroup(20); // 创建工作线程池
System.out.println("服务器启动成功,监听端口为:" + HostInfo.PORT);
try {
// 创建一个服务器端的程序类进行NIO启动,同时可以设置Channel
ServerBootstrap serverBootstrap = new ServerBootstrap(); // 服务器端
// 设置要使用的线程池以及当前的Channel类型
serverBootstrap.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class);
// 接收到信息之后需要进行处理,于是定义子处理器
serverBootstrap.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
socketChannel.pipeline().addLast(new LengthFieldBasedFrameDecoder(65536,0,4,0,4)) ;
socketChannel.pipeline().addLast(new MessagePackDecoder()) ;
socketChannel.pipeline().addLast(new LengthFieldPrepender(4)) ; // 与属性个数保持一致
socketChannel.pipeline().addLast(new MessagePackEncoder()) ;
socketChannel.pipeline().addLast(new EchoServerHandler()); // 追加了处理器
}
});
// 可以直接利用常亮进行TCP协议的相关配置
serverBootstrap.option(ChannelOption.SO_BACKLOG, 128);
serverBootstrap.childOption(ChannelOption.SO_KEEPALIVE, true);
// ChannelFuture描述的时异步回调的处理操作
ChannelFuture future = serverBootstrap.bind(HostInfo.PORT).sync();
future.channel().closeFuture().sync();// 等待Socket被关闭
} finally {
workerGroup.shutdownGracefully() ;
bossGroup.shutdownGracefully() ;
}
}
}
EchoClient.java
package com.bijian.http.server.client;
import com.bijian.http.server.client.handler.EchoClientHandler;
import com.bijian.info.HostInfo;
import com.bijian.netty.serious.MessagePackDecoder;
import com.bijian.netty.serious.MessagePackEncoder;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import io.netty.handler.codec.LengthFieldPrepender;
import io.netty.handler.codec.serialization.ClassResolvers;
import io.netty.handler.codec.serialization.ObjectDecoder;
import io.netty.handler.codec.serialization.ObjectEncoder;
public class EchoClient {
public void run() throws Exception {
// 1、如果现在客户端不同,那么也可以不使用多线程模式来处理;
// 在Netty中考虑到代码的统一性,也允许你在客户端设置线程池
EventLoopGroup group = new NioEventLoopGroup(); // 创建一个线程池
try {
Bootstrap client = new Bootstrap(); // 创建客户端处理程序
client.group(group).channel(NioSocketChannel.class)
.option(ChannelOption.TCP_NODELAY, true) // 允许接收大块的返回数据
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
socketChannel.pipeline().addLast(new LengthFieldBasedFrameDecoder(65536,0,4,0,4)) ;
socketChannel.pipeline().addLast(new MessagePackDecoder()) ;
socketChannel.pipeline().addLast(new LengthFieldPrepender(4)) ; // 与属性个数保持一致
socketChannel.pipeline().addLast(new MessagePackEncoder()) ;
socketChannel.pipeline().addLast(new EchoClientHandler()); // 追加了处理器
}
});
ChannelFuture channelFuture = client.connect(HostInfo.HOST_NAME, HostInfo.PORT).sync();
channelFuture.channel().closeFuture().sync(); // 关闭连接
} finally {
group.shutdownGracefully();
}
}
}
MessagePack实现传输序列化与使用Java原生的序列化程序管理实现对象网络传输项目的变化如下:
先启动EchoServerMain.java,再启动EchoClientMain.java,运行效果如下所示:
4.Marshalling序列化实例
项目结构如下所示:
Member.java中的@Message注解去掉
MarshallingCodeFactory.java
package com.bijian.netty.serious;
import io.netty.handler.codec.marshalling.*;
import org.jboss.marshalling.MarshallerFactory;
import org.jboss.marshalling.Marshalling;
import org.jboss.marshalling.MarshallingConfiguration;
public class MarshallingCodeFactory {
public static MarshallingDecoder buildMarshallingDecoder() {
MarshallerFactory marshallerFactory = Marshalling.getProvidedMarshallerFactory("serial") ; // 获取原始的JDK序列化
MarshallingConfiguration configuration = new MarshallingConfiguration() ;
UnmarshallerProvider provider = new DefaultUnmarshallerProvider(marshallerFactory,configuration) ;
int maxSize = 1024 << 2 ; // 设置单个对象的最大长度
MarshallingDecoder decoder = new MarshallingDecoder(provider,maxSize) ;
return decoder ;
}
public static MarshallingEncoder buildMarshallingEncoder() {
MarshallerFactory marshallerFactory = Marshalling.getProvidedMarshallerFactory("serial") ; // 获取原始的JDK序列化
MarshallingConfiguration configuration = new MarshallingConfiguration() ;
MarshallerProvider provider = new DefaultMarshallerProvider(marshallerFactory,configuration) ;
MarshallingEncoder encoder = new MarshallingEncoder(provider) ;
return encoder ;
}
}
EchoServer.java
package com.bijian.http.server.server;
import com.bijian.http.server.server.handler.EchoServerHandler;
import com.bijian.info.HostInfo;
import com.bijian.netty.serious.MarshallingCodeFactory;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import io.netty.handler.codec.LengthFieldPrepender;
/**
* 实现了基础的线程池与网络连接的配置项
*/
public class EchoServer {
public void run() throws Exception { // 进行服务器端的启动处理
// 线程池是提升服务器性能的重要技术手段,利用定长的线程池可以保证核心线程的有效数量
// 在Netty之中线程池的实现分为两类:主线程池(接收客户端连接)、工作线程池(处理客户端连接)
EventLoopGroup bossGroup = new NioEventLoopGroup(10); // 创建接收线程池
EventLoopGroup workerGroup = new NioEventLoopGroup(20); // 创建工作线程池
System.out.println("服务器启动成功,监听端口为:" + HostInfo.PORT);
try {
// 创建一个服务器端的程序类进行NIO启动,同时可以设置Channel
ServerBootstrap serverBootstrap = new ServerBootstrap(); // 服务器端
// 设置要使用的线程池以及当前的Channel类型
serverBootstrap.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class);
// 接收到信息之后需要进行处理,于是定义子处理器
serverBootstrap.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
socketChannel.pipeline().addLast(MarshallingCodeFactory.buildMarshallingEncoder()) ;
socketChannel.pipeline().addLast(MarshallingCodeFactory.buildMarshallingDecoder()) ;
socketChannel.pipeline().addLast(new EchoServerHandler()); // 追加了处理器
}
});
// 可以直接利用常亮进行TCP协议的相关配置
serverBootstrap.option(ChannelOption.SO_BACKLOG, 128);
serverBootstrap.childOption(ChannelOption.SO_KEEPALIVE, true);
// ChannelFuture描述的时异步回调的处理操作
ChannelFuture future = serverBootstrap.bind(HostInfo.PORT).sync();
future.channel().closeFuture().sync();// 等待Socket被关闭
} finally {
workerGroup.shutdownGracefully() ;
bossGroup.shutdownGracefully() ;
}
}
}
EchoClient.java
package com.bijian.http.server.client;
import com.bijian.http.server.client.handler.EchoClientHandler;
import com.bijian.info.HostInfo;
import com.bijian.netty.serious.MarshallingCodeFactory;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import io.netty.handler.codec.LengthFieldPrepender;
public class EchoClient {
public void run() throws Exception {
// 1、如果现在客户端不同,那么也可以不使用多线程模式来处理;
// 在Netty中考虑到代码的统一性,也允许你在客户端设置线程池
EventLoopGroup group = new NioEventLoopGroup(); // 创建一个线程池
try {
Bootstrap client = new Bootstrap(); // 创建客户端处理程序
client.group(group).channel(NioSocketChannel.class)
.option(ChannelOption.TCP_NODELAY, true) // 允许接收大块的返回数据
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
socketChannel.pipeline().addLast(MarshallingCodeFactory.buildMarshallingEncoder()) ;
socketChannel.pipeline().addLast(MarshallingCodeFactory.buildMarshallingDecoder()) ;
socketChannel.pipeline().addLast(new EchoClientHandler()); // 追加了处理器
}
});
ChannelFuture channelFuture = client.connect(HostInfo.HOST_NAME, HostInfo.PORT).sync();
channelFuture.channel().closeFuture().sync(); // 关闭连接
} finally {
group.shutdownGracefully();
}
}
}
Marshalling序列化与MessagePack实现传输序列化的变化如下:
echo-netty下的pom.xml
echo-netty下的pom.xml完整内容如下:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>echo</artifactId>
<groupId>cn.mldn</groupId>
<version>1.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>echo-netty</artifactId>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
</dependency>
<dependency>
<groupId>cn.mldn</groupId>
<artifactId>echo-util</artifactId>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jboss.marshalling</groupId>
<artifactId>jboss-marshalling</artifactId>
</dependency>
<dependency>
<groupId>org.jboss.marshalling</groupId>
<artifactId>jboss-marshalling-serial</artifactId>
</dependency>
</dependencies>
</project>
工程下的pom.xml
pom.xml完整内容如下:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>cn.mldn</groupId>
<artifactId>echo</artifactId>
<version>1.0</version>
<modules>
<module>echo-base</module>
<module>echo-util</module>
<module>echo-netty</module>
</modules>
<name>netty</name>
<!-- FIXME change it to the project's website -->
<url>http://www.example.com</url>
<packaging>pom</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<jdk.version>1.8</jdk.version>
<maven-compiler-plugin.version>3.7.0</maven-compiler-plugin.version>
<junit.version>4.12</junit.version>
<netty.version>4.1.31.Final</netty.version>
<echo.version>1.0</echo.version>
<msgpack.version>0.6.12</msgpack.version>
<jboss-marshalling.version>2.0.6.Final</jboss-marshalling.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>cn.mldn</groupId>
<artifactId>echo-util</artifactId>
<version>${echo.version}</version>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>${netty.version}</version>
</dependency>
<dependency>
<groupId>org.jboss.marshalling</groupId>
<artifactId>jboss-marshalling</artifactId>
<version>${jboss-marshalling.version}</version>
</dependency>
<dependency>
<groupId>org.jboss.marshalling</groupId>
<artifactId>jboss-marshalling-serial</artifactId>
<version>${jboss-marshalling.version}</version>
</dependency>
<dependency>
<groupId>org.msgpack</groupId>
<artifactId>msgpack</artifactId>
<version>${msgpack.version}</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>${maven-compiler-plugin.version}</version>
<configuration>
<source>${jdk.version}</source><!-- 源代码使用的开发版本 -->
<target>${jdk.version}</target><!-- 需要生成的目标class文件的编译版本 -->
</configuration>
</plugin>
</plugins>
</build>
</project>
先启动EchoServerMain.java,再启动EchoClientMain.java,运行效果如下所示:
5.JSON序列化实例
项目结构如下:
JSONDecoder.java
package com.bijian.http.server.serious;
import com.bijian.vo.Member;
import com.alibaba.fastjson.JSON;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToMessageDecoder;
import java.util.List;
public class JSONDecoder extends MessageToMessageDecoder<ByteBuf> {
@Override
protected void decode(ChannelHandlerContext channelHandlerContext, ByteBuf msg, List<Object> list) throws Exception {
int len = msg.readableBytes(); // 可以用的数据长度
byte data[] = new byte[len];
msg.getBytes(msg.readerIndex(), data, 0, len);
list.add(JSON.parseObject(new String(data)).toJavaObject(Member.class)) ;
}
}
JSONEncoder.java
package com.bijian.http.server.serious;
import com.alibaba.fastjson.JSONObject;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToByteEncoder;
public class JSONEncoder extends MessageToByteEncoder<Object> {
@Override
protected void encode(ChannelHandlerContext channelHandlerContext, Object msg, ByteBuf out) throws Exception {
byte data [] = JSONObject.toJSONString(msg).getBytes() ;
out.writeBytes(data) ;
}
}
EchoServer.java
package com.bijian.http.server.server;
import com.bijian.http.server.server.handler.EchoServerHandler;
import com.bijian.info.HostInfo;
import com.bijian.http.server.serious.JSONDecoder;
import com.bijian.http.server.serious.JSONEncoder;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import io.netty.handler.codec.LengthFieldPrepender;
/**
* 实现了基础的线程池与网络连接的配置项
*/
public class EchoServer {
public void run() throws Exception { // 进行服务器端的启动处理
// 线程池是提升服务器性能的重要技术手段,利用定长的线程池可以保证核心线程的有效数量
// 在Netty之中线程池的实现分为两类:主线程池(接收客户端连接)、工作线程池(处理客户端连接)
EventLoopGroup bossGroup = new NioEventLoopGroup(10); // 创建接收线程池
EventLoopGroup workerGroup = new NioEventLoopGroup(20); // 创建工作线程池
System.out.println("服务器启动成功,监听端口为:" + HostInfo.PORT);
try {
// 创建一个服务器端的程序类进行NIO启动,同时可以设置Channel
ServerBootstrap serverBootstrap = new ServerBootstrap(); // 服务器端
// 设置要使用的线程池以及当前的Channel类型
serverBootstrap.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class);
// 接收到信息之后需要进行处理,于是定义子处理器
serverBootstrap.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
socketChannel.pipeline().addLast(new LengthFieldBasedFrameDecoder(65536,0,4,0,4)) ;
socketChannel.pipeline().addLast(new JSONDecoder()) ;
socketChannel.pipeline().addLast(new LengthFieldPrepender(4)) ;
socketChannel.pipeline().addLast(new JSONEncoder()) ;
socketChannel.pipeline().addLast(new EchoServerHandler()); // 追加了处理器
}
});
// 可以直接利用常亮进行TCP协议的相关配置
serverBootstrap.option(ChannelOption.SO_BACKLOG, 128);
serverBootstrap.childOption(ChannelOption.SO_KEEPALIVE, true);
// ChannelFuture描述的时异步回调的处理操作
ChannelFuture future = serverBootstrap.bind(HostInfo.PORT).sync();
future.channel().closeFuture().sync();// 等待Socket被关闭
} finally {
workerGroup.shutdownGracefully() ;
bossGroup.shutdownGracefully() ;
}
}
}
EchoClient.java
package com.bijian.http.server.client;
import com.bijian.http.server.client.handler.EchoClientHandler;
import com.bijian.info.HostInfo;
import com.bijian.http.server.serious.JSONDecoder;
import com.bijian.http.server.serious.JSONEncoder;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import io.netty.handler.codec.LengthFieldPrepender;
public class EchoClient {
public void run() throws Exception {
// 1、如果现在客户端不同,那么也可以不使用多线程模式来处理;
// 在Netty中考虑到代码的统一性,也允许你在客户端设置线程池
EventLoopGroup group = new NioEventLoopGroup(); // 创建一个线程池
try {
Bootstrap client = new Bootstrap(); // 创建客户端处理程序
client.group(group).channel(NioSocketChannel.class)
.option(ChannelOption.TCP_NODELAY, true) // 允许接收大块的返回数据
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
socketChannel.pipeline().addLast(new LengthFieldBasedFrameDecoder(65536, 0, 4, 0, 4));
socketChannel.pipeline().addLast(new JSONDecoder());
socketChannel.pipeline().addLast(new LengthFieldPrepender(4));
socketChannel.pipeline().addLast(new JSONEncoder());
socketChannel.pipeline().addLast(new EchoClientHandler()); // 追加了处理器
}
});
ChannelFuture channelFuture = client.connect(HostInfo.HOST_NAME, HostInfo.PORT).sync();
channelFuture.channel().closeFuture().sync(); // 关闭连接
} finally {
group.shutdownGracefully();
}
}
}
echo-netty下的pom.xml
工程下pom.xml内容修改如下:
先启动EchoServerMain.java,再启动EchoClientMain.java,效果如下: