Netty简介
Netty是由JBOSS提供的一个java开源框架,现为 Github上的独立项目。Netty提供异步的、事件驱动的网络应用程序框架和工具,用以快速开发高性能、高可靠性的网络服务器和客户端程序。
也就是说,Netty 是一个基于NIO的客户、服务器端编程框架,使用Netty 可以确保你快速和简单的开发出一个网络应用,例如实现了某种协议的客户、服务端应用。Netty相当于简化和流线化了网络应用的编程开发过程,例如:基于TCP和UDP的socket服务开发。
“快速”和“简单”并不用产生维护性或性能上的问题。Netty 是一个吸收了多种协议(包括FTP、SMTP、HTTP等各种二进制文本协议)的实现经验,并经过相当精心设计的项目。最终,Netty 成功的找到了一种方式,在保证易于开发的同时还保证了其应用的性能,稳定性和伸缩性。
Netty服务端
引入依赖
<!--netty依赖-->
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>4.1.20.Final</version>
</dependency>
<!-- marshalling -->
<dependency>
<groupId>org.jboss.marshalling</groupId>
<artifactId>jboss-marshalling-serial</artifactId>
<version>2.0.0.Beta2</version>
</dependency>
Netty服务端
package com.example.uts.netty;
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 lombok.extern.slf4j.Slf4j;
/**
* netty 服务端
*
* @author lanx
* @date 2022/3/20
*/
@Slf4j
public class NettyServer {
private static class SingletionHolder {
static final NettyServer instance = new NettyServer();
}
public static NettyServer getInstance() {
return SingletionHolder.instance;
}
//用户接收客户端连接的线程工作组
EventLoopGroup bossGroup;
//用于接收客户端连接读写操作的线程组
EventLoopGroup workerGroup;
//辅助类 帮我我们创建netty服务
ServerBootstrap b;
ChannelFuture cf;
private NettyServer() {
//用户接收客户端连接的线程工作组
bossGroup = new NioEventLoopGroup();
//用于接收客户端连接读写操作的线程组
workerGroup = new NioEventLoopGroup();
//辅助类 帮我我们创建netty服务
b = new ServerBootstrap();
b.group(bossGroup, workerGroup)//绑定两个工作组
.channel(NioServerSocketChannel.class)//设置NIO模式
//option 针对于服务端配置; childOption 针对于客户端连接通道配置
.option(ChannelOption.SO_BACKLOG, 1024)//设置tcp缓冲区
.childOption(ChannelOption.SO_SNDBUF, 32 * 1024)//设置发送数据的缓存大小
.childOption(ChannelOption.SO_RCVBUF, 32 * 1024)//设置读取数据的缓存大小
.childOption(ChannelOption.SO_KEEPALIVE, true)//设置保持长连接
//初始化绑定服务通道
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel sc) throws Exception {
//序列化
//在实际处理前进行编解码,服务器端接收消息先解码,然后返回消息的时候编码【约定】
sc.pipeline().addLast(MarshallingCodeCfactory.buildMarshallingDecoder());
sc.pipeline().addLast(MarshallingCodeCfactory.buildMarshallingEecoder());
//为通道进行初始化:数据传输过来的时候会进行拦截和执行 (可以有多个拦截器)
sc.pipeline().addLast(new NettyServerHandler());
}
});
}
/**
* 启动服务
*/
public void start(int port) {
this.cf = b.bind(port);
log.info("服务启动...........");
}
public ChannelFuture getChannelFuture() {
return this.cf;
}
/**
* 关闭连接
*
* @throws InterruptedException
*/
public void close() throws InterruptedException {
//释放连接
this.cf.channel().closeFuture().sync();
this.bossGroup.shutdownGracefully();
this.workerGroup.shutdownGracefully();
}
}
package com.example.uts.netty;
import com.alibaba.fastjson.JSON;
import com.example.uts.domain.RespData;
import com.example.uts.domain.ResqData;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.util.ReferenceCountUtil;
import java.util.UUID;
/**
* 服务端 监听器
*
* @author lanx
* @date 2022/3/20
*/
public class NettyServerHandler extends ChannelInboundHandlerAdapter {
/**
* 当我们的通道被激活的时候触发的监听
*
* @param ctx
* @throws Exception
*/
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
System.out.println("--------服务端通道激活---------");
}
/**
* 当我们通道里有数据进行读取的时候触发的监听
*
* @param ctx netty服务上下文
* @param msg 实际传输的数据
* @throws Exception
*/
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
try {
ResqData resqData = (ResqData)msg;
System.out.println("服务端:获取客户端数据-->"+ JSON.toJSONString(resqData));
RespData respData = new RespData();
respData.setId(UUID.randomUUID().toString());
respData.setCode(200);
respData.setData(resqData.getData());
//响应给客户端的数据
ctx.writeAndFlush(respData);
}catch (Exception e){
e.printStackTrace();
}finally {
//释放数据
ReferenceCountUtil.release(msg);
}
}
/**
* 当我们读取完成数据的时候触发的监听
*
* @param ctx
* @throws Exception
*/
@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
System.out.println("--------服务端数据读取完毕---------");
}
/**
* 当我们读取数据异常的时候触发的监听
*
* @param ctx
* @param cause
* @throws Exception
*/
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
System.out.println("--------服务端数据读取异常---------");
cause.printStackTrace();
ctx.close();
}
}
package com.example.uts.netty;
import io.netty.handler.codec.marshalling.*;
import org.jboss.marshalling.MarshallerFactory;
import org.jboss.marshalling.Marshalling;
import org.jboss.marshalling.MarshallingConfiguration;
/**
* Marshalling 工厂类
*
* @author lanx
* @date 2022/3/22
*/
public final class MarshallingCodeCfactory {
/**
* 创建Jboss Marshalling 解码器 MarshallingDecoder
* @return
*/
public static MarshallingDecoder buildMarshallingDecoder() {
//首先通过Marshalling 工具类的静态方法获取Marshalling实例对象,参数serial标识创建的是java序列化工厂对象
final MarshallerFactory marshallerFactory = Marshalling.getProvidedMarshallerFactory("serial");
//创建 MarshallingConfiguration对象,配置了版本号为5
final MarshallingConfiguration configuration = new MarshallingConfiguration();
configuration.setVersion(5);
//根据MarshallerFactory和MarshallingConfiguration 创建 provider
UnmarshallerProvider provider = new DefaultUnmarshallerProvider(marshallerFactory, configuration);
//构建Netty的MarshallingDecoder对象,两个参数分别是provider和单个消息序列化后的最大长度
MarshallingDecoder decoder = new MarshallingDecoder(provider, 1024 * 1024 * 1);
return decoder;
}
/**
* 创建Jboss Marshalling 编码器 MarshallingEncoder
* @return
*/
public static MarshallingEncoder buildMarshallingEecoder() {
//首先通过Marshalling 工具类的静态方法获取Marshalling实例对象,参数serial标识创建的是java序列化工厂对象
final MarshallerFactory marshallerFactory = Marshalling.getProvidedMarshallerFactory("serial");
//创建 MarshallingConfiguration对象,配置了版本号为5
final MarshallingConfiguration configuration = new MarshallingConfiguration();
configuration.setVersion(5);
//根据MarshallerFactory和MarshallingConfiguration 创建 provider
MarshallerProvider provider = new DefaultMarshallerProvider(marshallerFactory, configuration);
//构建Netty的MarshallingEncoder对象,MarshallingEncoder用于实现序列化接口的POJO对象序列化为二进制数组
MarshallingEncoder encoder = new MarshallingEncoder(provider);
return encoder;
}
}
package com.example.uts.listener;
import com.example.uts.netty.NettyServer;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.stereotype.Component;
/**
* 初始化netty客户端连接
* @author lanx
* @date 2022/3/27
*/
@Component
@Slf4j
public class SpringListener implements ApplicationListener<ContextRefreshedEvent> {
@Value("${netty.port}")
private int port;
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
if(event.getApplicationContext().getParent() == null){
//需要执行的业务代码,当spring 容器初始化完成后执行
NettyServer.getInstance().start(port);
log.info("spring 加载完成...");
}
}
}
Netty 客户端
package com.example.uts.netty;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
/**
* @author lanx
* @date 2022/3/27
*/
public class NettyClient {
private static class SingletionHolder {
static final NettyClient instance = new NettyClient();
}
public static NettyClient getInstance(){
return SingletionHolder.instance;
}
private String host ;
private int port ;
//线程工作组
EventLoopGroup workerGroup;
//辅助类 帮我我们创建netty服务
Bootstrap b ;
ChannelFuture cf;
private NettyClient(){
//线程工作组
workerGroup = new NioEventLoopGroup();
//辅助类 帮我我们创建netty服务
b = new Bootstrap();
b.group(workerGroup)//绑定两个工作组
.channel(NioSocketChannel.class)//设置NIO模式
//初始化绑定服务通道
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel sc) throws Exception {
//序列化
sc.pipeline().addLast(MarshallingCodeCfactory.buildMarshallingEecoder());
sc.pipeline().addLast(MarshallingCodeCfactory.buildMarshallingDecoder());
//为通道进行初始化:数据传输过来的时候会进行拦截和执行 (可以有多个拦截器)
sc.pipeline().addLast(new NettyClientHandler());
}
});
}
public ChannelFuture getChannelFuture(){
//如果为null
if(this.cf == null){
this.connect();
}
//如果没有激活
if(!this.cf.channel().isActive()){
this.connect();
}
return this.cf;
}
/**
* 连接服务器
*/
public void connect(){
this.cf = b.connect(host, port).syncUninterruptibly();
System.out.println("远程服务端已经连接,可以数据传输");
}
/**
* 关闭连接
* @throws InterruptedException
*/
public void close() throws InterruptedException {
//释放连接
this.cf.channel().closeFuture().sync();
workerGroup.shutdownGracefully();
}
public String getHost() {
return host;
}
public void setHost(String host) {
this.host = host;
}
public int getPort() {
return port;
}
public void setPort(int port) {
this.port = port;
}
}
package com.example.uts.netty;
import com.alibaba.fastjson.JSON;
import com.example.uts.domain.RespData;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.util.ReferenceCountUtil;
import lombok.extern.slf4j.Slf4j;
/**
* 客户端 监听器
*
* @author lanx
* @date 2022/3/20
*/
@Slf4j
public class NettyClientHandler extends ChannelInboundHandlerAdapter {
/**
* 当我们的通道被激活的时候触发的监听
*
* @param ctx
* @throws Exception
*/
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
System.out.println("--------客户端通道激活---------");
}
/**
* 当我们通道里有数据进行读取的时候触发的监听
*
* @param ctx netty服务上下文
* @param msg 实际传输的数据
* @throws Exception
*/
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
try {
RespData respData = (RespData)msg;
log.info("客户端:获取服务端响应数据-->{}", JSON.toJSONString(respData));
} catch (Exception e) {
e.printStackTrace();
} finally {
//释放数据 (如果你读取数据后又写出去数据就不需要调用此方法,因为底层代码帮忙实现额释放数据)
ReferenceCountUtil.release(msg);
}
}
/**
* 当我们读取完成数据的时候触发的监听
*
* @param ctx
* @throws Exception
*/
@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
System.out.println("--------客户端数据读取完毕---------");
}
/**
* 当我们读取数据异常的时候触发的监听
*
* @param ctx
* @param cause
* @throws Exception
*/
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
System.out.println("--------客户端数据读取异常---------");
cause.printStackTrace();
ctx.close();
}
}
package com.example.uts.netty;
import io.netty.handler.codec.marshalling.*;
import org.jboss.marshalling.MarshallerFactory;
import org.jboss.marshalling.Marshalling;
import org.jboss.marshalling.MarshallingConfiguration;
/**
* Marshalling 工厂类
*
* @author lanx
* @date 2022/3/22
*/
public final class MarshallingCodeCfactory {
/**
* 创建Jboss Marshalling 解码器 MarshallingDecoder
* @return
*/
public static MarshallingDecoder buildMarshallingDecoder() {
//首先通过Marshalling 工具类的静态方法获取Marshalling实例对象,参数serial标识创建的是java序列化工厂对象
final MarshallerFactory marshallerFactory = Marshalling.getProvidedMarshallerFactory("serial");
//创建 MarshallingConfiguration对象,配置了版本号为5
final MarshallingConfiguration configuration = new MarshallingConfiguration();
configuration.setVersion(5);
//根据MarshallerFactory和MarshallingConfiguration 创建 provider
UnmarshallerProvider provider = new DefaultUnmarshallerProvider(marshallerFactory, configuration);
//构建Netty的MarshallingDecoder对象,两个参数分别是provider和单个消息序列化后的最大长度
MarshallingDecoder decoder = new MarshallingDecoder(provider, 1024 * 1024 * 1);
return decoder;
}
/**
* 创建Jboss Marshalling 编码器 MarshallingEncoder
* @return
*/
public static MarshallingEncoder buildMarshallingEecoder() {
//首先通过Marshalling 工具类的静态方法获取Marshalling实例对象,参数serial标识创建的是java序列化工厂对象
final MarshallerFactory marshallerFactory = Marshalling.getProvidedMarshallerFactory("serial");
//创建 MarshallingConfiguration对象,配置了版本号为5
final MarshallingConfiguration configuration = new MarshallingConfiguration();
configuration.setVersion(5);
//根据MarshallerFactory和MarshallingConfiguration 创建 provider
MarshallerProvider provider = new DefaultMarshallerProvider(marshallerFactory, configuration);
//构建Netty的MarshallingEncoder对象,MarshallingEncoder用于实现序列化接口的POJO对象序列化为二进制数组
MarshallingEncoder encoder = new MarshallingEncoder(provider);
return encoder;
}
}
package com.example.uts.listener;
import com.example.uts.netty.NettyClient;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.stereotype.Component;
/**
* 初始化netty客户端连接
* @author lanx
* @date 2022/3/27
*/
@Component
@Slf4j
public class SpringListener implements ApplicationListener<ContextRefreshedEvent> {
@Value("${netty.port}")
private int port;
@Value("${netty.host}")
private String host;
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
if(event.getApplicationContext().getParent() == null){
//需要执行的业务代码,当spring 容器初始化完成后执行
log.info("spring 加载完成...");
NettyClient nettyClient = NettyClient.getInstance();
nettyClient.setHost(host);
nettyClient.setPort(port);
nettyClient.connect();
}
}
}