Java IO -Netty(10分钟帮你快速了解Netty)

 

Netty 入门指南

引言

在现代的网络应用开发中,Netty作为一个高性能的网络编程框架,已经成为了Java开发者的首选。本篇文章将简单介绍下Netty的基本使用。

Netty简介

Netty是一个基于Java NIO的异步事件驱动的网络应用框架,它提供了对协议的简单编码和解码,用于快速开发可维护的高性能和高可靠性的网络服务器和客户端程序。

核心组件

  • Channel:IO 操作的通道,NIO 中Channel 的概念  👉Java IO-Channels
  • EventLoop:负责处理一个或多个 Channel 的 I/O 事件,每一个EventLoop 由一个线程和Selector 组成。
  • EventLoopGroup:它是一组 EventLoop 的集合,管理者多个EventLoop
  • ChannelHandler:用于具体操作或拦截Channel中的事件和操作的接口。
  • ChannelPipeline:一个ChannelHandler的链表,将多了ChannelHandler链接起来,形成责任链模式,每个ChannelHandler依次处理事件。


简单编写套路

以一个简单的Echo服务器为例,展示下Netty的基础用法。

  1. // Echo服务器示例代码
  2. public class EchoServer {
  3. public static void main(String[] args) {
  4. try {
  5. ServerBootstrap b = new ServerBootstrap();
  6. b.group(new NioEventLoopGroup(), new NioEventLoopGroup())
  7. .channel(NioServerSocketChannel.class)
  8. .childHandler(new ChannelInitializer<SocketChannel>() {
  9. @Override
  10. public void initChannel(SocketChannel ch) {
  11. ChannelPipeline p = ch.pipeline();
  12. p.addLast(new EchoServerHandler());
  13. }
  14. });
  15. ChannelFuture f = b.bind(8080).sync();
  16. f.channel().closeFuture().sync();
  17. } finally {
  18. // ...
  19. }
  20. }
  21. }
  22. // Echo服务器处理器
  23. class EchoServerHandler extends ChannelInboundHandlerAdapter {
  24. @Override
  25. public void channelRead(ChannelHandlerContext ctx, Object msg) {
  26. ctx.write(msg);
  27. }
  28. // ...
  29. }

编写Echo客户端

以下是一个简单的Echo客户端示例,用于展示如何使用Netty进行网络请求。

  1. // Echo客户端示例代码
  2. public class EchoClient {
  3. public static void main(String[] args) {
  4. try {
  5. Bootstrap b = new Bootstrap();
  6. b.group(new NioEventLoopGroup())
  7. .channel(NioSocketChannel.class)
  8. .handler(new ChannelInitializer<SocketChannel>() {
  9. @Override
  10. public void initChannel(SocketChannel ch) {
  11. ChannelPipeline p = ch.pipeline();
  12. p.addLast(new EchoClientHandler());
  13. }
  14. });
  15. ChannelFuture f = b.connect("localhost", 8080).sync();
  16. // ...
  17. } finally {
  18. // ...
  19. }
  20. }
  21. }
  22. // Echo客户端处理器
  23. class EchoClientHandler extends ChannelInboundHandlerAdapter {
  24. @Override
  25. public void channelActive(ChannelHandlerContext ctx) {
  26. ctx.writeAndFlush(Unpooled.copiedBuffer("Hello, World!", CharsetUtil.UTF_8));
  27. }
  28. }

代码通俗解释

服务端代码

  1. 创建一个跟节点

    1. ServerBootstrap b = new ServerBootstrap();
  2. 绑定两个EventLoop组,这里为什么绑定两个呢?官方说一个用于处理客户端的连接请求,一个用于接受的连接之后的所有事件。这里如果搞一个 EventLoopGroup 怎么样? 当然可以!!!无非就是socket 链接自己处理呗!通用的代码,就不用每个人都写了。

    1. b.group(new NioEventLoopGroup(), new NioEventLoopGroup())
  3. 选择用的IO通道方式,选择 Streams,Channels,阻塞的方式有 BlockUnBlock,那么IO通信的通道方式一共有4种,Netty选择的是?在Java AIO的体系结构中用Channel就一定不阻塞?看这里。 这里只是Netty的实现,但不是金科律令。为什么? 请查看  👉Java IO-Streams, 👉Java IO-Channels

    1. .channel(NioSocketChannel.class)
  4. 固定写法 没什么好解释的,这一步开始通过SocketChannel处理传输过来的信息!!!

    1. .handler(new ChannelInitializer<SocketChannel>() {
    2. @Override
    3. public void initChannel(SocketChannel ch) {
    4. ChannelPipeline p = ch.pipeline();
    5. p.addLast(new EchoClientHandler());
    6. }
    7. });
  5. 通过channel 拿到处理责任链 ChannelPipeline

    1. ChannelPipeline p = ch.pipeline();
  6. 在处理责任链中添加具体的处理过程,可以添加多个及特定的处理

    1. p.addLast(new EchoClientHandler());
  7. handler实现,继承ChannelXxxHandlerAdapter,实现所需业务方法即可

    1. class EchoServerHandler extends ChannelInboundHandlerAdapter {
    2. @Override
    3. public void channelRead(ChannelHandlerContext ctx, Object msg) {
    4. ctx.write(msg);
    5. }
    6. }
  8. 绑定端口,直到服务起来,同步阻塞,单个线程休眠 直至服务启动起来。这里可以async()?

    1. ChannelFuture f = b.bind(8080).sync();
  9. 线程阻塞等待服务关闭,进行后续资源处理。这里可以async()?

    1. f.channel().closeFuture().sync();

客户端代码

同理,略

疑惑🤔

EventLoop

负责处理一个或多个 Channel 的 I/O 事件,每一个EventLoop 由一个线程和Selector 组成

  1. // 伪代码
  2. public class SimpleEventLoop extends Thread {
  3. private final Selector selector;
  4. public SimpleEventLoop(Selector selector) {
  5. try {
  6. this.selector =selector;
  7. } catch (IOException e) {
  8. throw new RuntimeException("Failed to open selector", e);
  9. }
  10. }
  11. @Override
  12. public void run() {
  13. while (!Thread.currentThread().isInterrupted()) {
  14. try {
  15. // 等待感兴趣的事件发生,这里使用无限期等待
  16. selector.select();
  17. // 获取所有准备好的SelectionKey
  18. Set<SelectionKey> selectedKeys = selector.selectedKeys();
  19. handleSelectedKeys(selectedKeys);
  20. } catch (IOException e) {
  21. // 处理可能的IOException
  22. e.printStackTrace();
  23. }
  24. }
  25. }
  26. private void handleSelectedKeys(Set<SelectionKey> keys) {
  27. Iterator<SelectionKey> it = keys.iterator();
  28. while (it.hasNext()) {
  29. SelectionKey key = it.next();
  30. it.remove();
  31. if (!key.isValid()) {
  32. continue;
  33. }
  34. if (key.isAcceptable()) {
  35. handleAccept(key);
  36. } else if (key.isReadable()) {
  37. handleRead(key);
  38. }
  39. }
  40. }
  41. }

Selector

EventLoop中的selector从那里获得的事件?NIO。 挖个坑 后续补充下

  1. xxxChannel1.register(selector, SelectionKey.OP_READ);
  2. xxxChannel2.register(selector, SelectionKey.OP_READ);

结语

Netty是一个功能强大且易于使用的网络编程框架。本文仅涵盖了入门知识,Netty还有许多高级特性等待您去探索。 通过

 👉Java IO-Streams

👉Java IO-Channels

👉Design Pattern - Observer Pattern

👉Design Pattern - Dispatcher Pattern

👉Design Pattern -High performance Dispatcher Pattern

循序渐进描述了IO的基本概念和Netty的轮廓,您还可以根据最基础的概念排列组合出来新的IO框架,从更高的Java视野这一层面上认识IO。为什么是Java视野?因为OS,未完待续。