从今天开始我们进入Netty系列。
一起探索下面几个问题,将使我们对Netty有一个初步的了解。
- 为什么都不使用大家都不用Java原生的Nio,Aio,Bio来实现服务架构了?而是使用Netty,那么它的优势是什么?
- Netty支持的协议有哪些?
- 为什么说Netty是事件驱动的异步模型?
- 如何开启一个Netty服务端?
Netty的优势是什么
Netty 是一个基于 Java 的高性能网络应用框架,它提供了一种简单、灵活、可扩展的方式来开发网络应用程序。
虽然 Java 原生的 NIO(New I/O)和 AIO(Asynchronous I/O)等API 也可以用于网络编程,但是 Netty 在以下几个方面提供了更多的优势,这也是为什么人们更喜欢使用 Netty 而不是 Java 原生库的原因:
简化了网络应用程序的开发
:Netty 提供了高度抽象的编程模型,使得开发人员能够更轻松地构建复杂的网络应用程序。它的设计理念是面向对象的,提供了一组易于使用的组件和工具,简化了网络编程的复杂性。高性能和可伸缩性
:Netty 在性能方面进行了优化,并提供了许多高级功能,如零拷贝技术和事件驱动的异步模型。这些优化使得 Netty 在处理高并发和大规模连接时表现出色,并且具有较低的资源消耗。更好的可维护性和扩展性
:Netty 的设计模式和组件使得代码更易于维护和扩展。它提供了一套强大的解码器和编码器,使得数据的处理和转换更加灵活和可扩展。此外,Netty 还支持插件机制,可以方便地集成其他第三方库和工具。更丰富的功能
:Netty 提供了丰富的功能集,如心跳检测、SSL/TLS 支持、WebSocket 支持、流量整形和拆包/粘包处理等。这些功能在实际应用中很常用,可以帮助开发人员更快速地构建高性能的网络应用程序。社区活跃和成熟度高
:Netty 是一个开源项目,拥有庞大的社区支持。它的开发团队经验丰富,有着长期的开发和维护历史。这意味着 Netty 在稳定性、Bug 修复和功能改进方面有着较高的可靠性。
Netty支持的协议有哪些?
TCP(传输控制协议)
:Netty 提供了强大的 TCP 协议支持,能够构建高性能的 TCP 服务器和客户端应用程序。UDP(用户数据报协议
):Netty 支持 UDP 协议,使得开发者可以轻松地构建基于 UDP 的应用程序,如实时音视频传输、游戏服务器等。HTTP(超文本传输协议)
:Netty 提供了全面的 HTTP 协议支持,包括 HTTP 客户端和服务器的实现。它支持 HTTP/1.0、HTTP/1.1 和 HTTP/2,能够处理 Web 请求和响应,并提供了丰富的 HTTP 相关功能。WebSocket
:Netty 支持 WebSocket 协议,使得开发者可以构建实时的双向通信应用程序,如聊天应用、实时消息推送等。SPDY
:Netty 提供了 SPDY 协议的支持,SPDY 是一种优化的网络传输协议,用于加速 Web 页面的加载速度。HTTP/2
:Netty 也支持 HTTP/2 协议,HTTP/2 是对 HTTP/1.1 的改进,提供了更高效的多路复用、头部压缩、服务器推送等特性,能够提升 Web 应用的性能。
除了以上列举的协议,Netty 还支持更多的网络协议,
如 SMTP(简单邮件传输协议)
、FTP(文件传输协议)
、DNS(域名系统协议)
等。
同时,Netty 还提供了灵活的 API 和可扩展的架构,开发者可以自定义协议
满足特定应用需求。
为什么说Netty是事件驱动的异步模型?
Netty 是异步事件驱动的框架,该框架体现为所有的I/O
操作都是异步的,所有的I/O
调用会立即返回,并不保证调用成功与否,但是调用会返回ChannelFuture
。Netty 会通过 ChannelFuture
通知调用是成功了还是失败了,亦或是取消了
当Future
对象刚刚创建时,处于非完成状态
,调用者可以通过返回的ChannelFuture
来获取操作执行的状态,再通过注册监听函数来执行完成后的操作
例:
代码语言:javascript复制future.addListener((ChannelFutureListener) callback -> {
boolean success = callback.isSuccess();
if (success) {
//操作SUCCESS
} else {
//操作失败的情况todo...
}
});
常见有如下操作:
- 通过
isDone
方法来判断当前操作是否完成。 - 通过
isSuccess
方法来判断已完成的当前操作是否成功。 - 通过
getCause
方法来获取已完成的当前操作失败的原因。 - 通过
isCancelled
方法来判断已完成的当前操作是否被取消。 - 通过
addListener
方法来注册监听器,当操作已完成(isDone
方法返回完成),将会通知指定的监听器;如果future
对象已完成,则通知指定监听器。
通过判断feature返回的状态 我们可以做对应的处理。
如何开启一个Netty服务端
要开启一个Netty服务端 首先在Java项目中 引入maven依赖;
代码语言:javascript复制 <dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>4.1.70.Final</version>
</dependency>
创建NioNettyServer
代码语言:javascript复制public class NioNettyServer {
@Value("${nio.netty.server.port: 10010}")
private int port;
@Autowired
private NettyServerChannelInitializer nettyServerChannelInitializer;
@Async
public void start() {
log.info("start to netty server port is {}", port);
// 接收连接
EventLoopGroup boss = new NioEventLoopGroup();
// 处理信息
EventLoopGroup worker = new NioEventLoopGroup();
try {
// 定义server
ServerBootstrap serverBootstrap = new ServerBootstrap();
// 添加分组
serverBootstrap.group(boss, worker)
// 添加通道设置非阻塞
.channel(NioServerSocketChannel.class)
// 服务端可连接队列数量
.option(ChannelOption.SO_BACKLOG, 128)
// 开启长连接
.childOption(ChannelOption.SO_KEEPALIVE, Boolean.TRUE)
// 流程处理
.childHandler(nettyServerChannelInitializer);
// 绑定端口
ChannelFuture cf = serverBootstrap.bind(port).sync();
// 优雅关闭连接
cf.channel().closeFuture().sync();
} catch (Exception e) {
log.error("connection error:{}", e.getMessage(), e);
} finally {
boss.shutdownGracefully();
worker.shutdownGracefully();
}
}
}
创建NettyServerChannelInitializer
代码语言:javascript复制@Component
@Slf4j
public class NettyServerChannelInitializer extends ChannelInitializer {
@Autowired
private ClientMessageHandler clientMessageHandler;
@Override
protected void initChannel(Channel channel) {
// 设置编码类型
channel.pipeline().addLast("decoder",new HexDecoder());
// 设置解码类型
channel.pipeline().addLast("encoder",new HexEncoder());
// 消息业务处理
channel.pipeline().addLast("ClientMessageHandler",clientMessageHandler);
}
}
创建消息处理Handler
代码语言:javascript复制public class ClientMessageHandler extends SimpleChannelInboundHandler<String> {
/**
* 设备接入连接时处理
*
* @param ctx
*/
@Override
public void handlerAdded(ChannelHandlerContext ctx) throws RedisConnectException {
Channel channel = ctx.channel();
log.info("netty有新的连接:[{}]", channel.remoteAddress());
}
/**
* 设备有新的数据发送过来时
*
* @param ctx
*/
@Override
public void channelRead0(ChannelHandlerContext ctx, String msg) throws RedisConnectException {
log.info("netty有新的数据:[{}]", msg);
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
Channel channel = ctx.channel();
if (cause instanceof IllegalArgumentException) {
log.warn("业务异常请排查, {}", cause.getMessage(), cause);
}
log.error("error message {},channel:{}", cause.getMessage(), channel.remoteAddress());
}
/**
* 断开连接
* @param ctx s下文
*/
@Override
public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
Channel channel = ctx.channel();
log.error("netty客户端-断开连接,ip:{}", channel.remoteAddress());
}
}
在启动类中添加:
代码语言:javascript复制public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(App.class, args);
context.getBean(NioNettyServer.class).start();
}
启动后日志输出如下信息,证明netty服务端启动成功。
总结:
Netty 的设计思想强调了可重用性
、灵活性
和可扩展性
。它提供了丰富的 API,包括对各种网络协议的支持,如 TCP、UDP、HTTP、WebSocket 等。开发人员可以通过简单的配置和自定义处理器来构建自己的网络应用程序,并根据具体需求进行灵活的定制。
Netty 还提供了许多高级功能,如高性能的序列化框架
、心跳机制
、SSL/TLS 支持
、拆包
与粘包
处理、流量控制
等,使得开发人员能够快速地构建安全、可靠且高效的网络应用程序。