Начало работы с Нетти

Java задняя часть сервер Netty

Оригинальный адрес блога:блог Пимайка

предисловие

Эта статья в основном знакомит с примером кода клиента и сервера Netty, имеет интуитивное представление о Netty и показывает, как использовать Netty.В последующих статьях будет подробно проанализирован каждый компонент Netty.

Введение в Нетти

Netty — это асинхронная среда веб-приложений, управляемая событиями, которая поддерживает быструю разработку поддерживаемых приложений.высокая производительностьпротокол-ориентированный сервер и клиент. Netty в основном инкапсулирует пакет nio для Java.

Зачем использовать Нетти

Представленная выше, Нетти являетсяВысокопроизводительная сетевая коммуникационная среда, то почему мы должны использовать Netty, другими словами, каковы преимущества Netty, которые заставляют нас использовать его, почему бы не использовать собственное программирование Java Socket или использовать Java NIO, представленный в Java 1.4. Затем проанализируйте и проанализируйте программирование Java Socket и Java NIO.

Сетевое программирование на Java

Давайте сначала рассмотрим пример сетевого программирования на Java:

public static void main(String[] args) {
        try {
            ServerSocket serverSocket = new ServerSocket(8888);
            Socket socket = serverSocket.accept();
            BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            String line = "";
            while ((line = reader.readLine()) != null) {
                System.out.println("received: " + line);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
}

Выше показан пример простого сервера сокетов. Код может одновременно обрабатывать только одно соединение. Чтобы управлять несколькими одновременными клиентами, необходимо создать новый поток для каждого нового сокета клиента. Эта схема параллелизма приемлема для небольшого и среднего количества клиентов. Если она предназначена для высокой параллелизма, более 100 000 одновременных подключений, эта схема не рекомендуется. Она требует слишком много ресурсов потоков и может существовать в любое время. Большое количество потоки находятся в заблокированном состоянии, ожидая готовности входных или выходных данных, а производительность всего решения слишком низкая. Следовательно, в сценариях с высокой степенью параллелизма общие решения для сетевого программирования на Java не рекомендуются.

Java NIO

Давайте сначала рассмотрим пример Java NIO:

public class ServerSocketChannelDemo {
    private ServerSocketChannel serverSocketChannel;
    private Selector selector;

    public ServerSocketChannelDemo(int port) throws IOException {
        serverSocketChannel = ServerSocketChannel.open();
        serverSocketChannel.configureBlocking(false);
        serverSocketChannel.socket().bind(new InetSocketAddress(port));
        selector = Selector.open();
        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
    }

    public void listener() throws IOException {
        while (true) {
            int n = selector.select();
            if (n == 0) {
                continue;
            }
            Iterator iterator = selector.selectedKeys().iterator();
            while (iterator.hasNext()) {
                SelectionKey selectionKey = (SelectionKey) iterator.next();
                if (selectionKey.isAcceptable()) {
                    ServerSocketChannel server_channel = (ServerSocketChannel) selectionKey.channel();
                    SocketChannel socketChannel = server_channel.accept();
                    socketChannel.configureBlocking(false);
                    socketChannel.register(selector, SelectionKey.OP_READ);
                }
                if (selectionKey.isReadable()) {
                    //如果通道处于读就绪的状态
                    //读操作
                    //TODO
                }
            }
        }
    }
}

Основные части NIO в основном включают:

  • канал
  • Буфер Буфер
  • Селектор мультиплексора

Селектор Селектор является ключом к реализации неблокирующего ввода/вывода в Java.Зарегистрируйте канал канала в селекторе.Если канал канала отправляет событие чтения или записи, канал находится в состоянии готовности и будет опрошен Селектор для последующей операции ввода/вывода. Этот метод мультиплексирования ввода-вывода обеспечивает лучшее управление ресурсами, чем описанная выше модель блокирующего ввода-вывода:

  • Многие соединения могут обрабатываться с меньшим количеством потоков, что снижает накладные расходы на управление памятью и переключение контекста.
  • Потоки также можно использовать для других задач, когда не нужно обрабатывать операции ввода-вывода.

Хотя использование Java NIO позволяет нам обрабатывать множество соединений с меньшим количеством потоков,Надежная и эффективная обработка и планирование операций ввода-вывода при высокой нагрузке — утомительная и подверженная ошибкам задача., так что это привело к высокопроизводительным специалистам по сетевому программированию——Netty

Особенности сети

У Netty есть много отличных функций, которые стоит использовать (от Netty в действии):

дизайн

  • Единый API для разных протоколов (блокирующих и неблокирующих)
  • На основе гибкой и расширяемой событийно-управляемой модели
  • Настраиваемая модель потоковой передачи
  • Надежная поддержка сокет данных без подключения без подключения (UDP)

представление

  • лучшая пропускная способность, низкая задержка
  • меньшее потребление ресурсов
  • минимальная копия памяти

прочность

  • Больше не вызывает OutOfMemoryError для слишком быстрых, слишком медленных или перегруженных соединений.
  • Больше нет проблемы непостоянных частот чтения и записи NIO в высокоскоростных сетевых средах.

безопасность:

  • Полная поддержка SSL/TLS и STARTTLS
  • Может использоваться в ограниченных средах, таких как апплеты и OSGI.

легко использовать:

  • Подробный Javadoc и большой набор примеров
  • Никаких зависимостей, кроме JDK 1.6+, не требуется.

Пример кода Netty

Ниже приведен пример кода сервера и клиента. Давайте сначала рассмотрим код Netty. В последующих статьях будет подробно проанализирован каждый модуль.

Код сервера

public class EchoServer {
    private final int port;

    public EchoServer(int port) {
        this.port = port;
    }

    public static void main(String[] args) throws InterruptedException {
        new EchoServer(8888).start();
    }

    public void start() throws InterruptedException {
        final EchoServerHandler serverHandler = new EchoServerHandler();
        //创建EventLoopGroup,处理事件
        EventLoopGroup boss = new NioEventLoopGroup();
        EventLoopGroup worker = new NioEventLoopGroup();
        try {
            ServerBootstrap b = new ServerBootstrap();
            b.group(boss,worker)
                    //指定所使用的NIO传输 Channel
                    .channel(NioServerSocketChannel.class)
                    //使用指定的端口设置套接字地址
                    .localAddress(new InetSocketAddress(port))
                    //添加一个EchoServerHandler到子Channel的ChannelPipeline
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel socketChannel) throws Exception {
                            //EchoServerHandler标志为@Shareable,所以我们可以总是使用同样的实例
                            socketChannel.pipeline().addLast(serverHandler);
                        }
                    });
            //异步的绑定服务器,调用sync()方法阻塞等待直到绑定完成
            ChannelFuture future = b.bind().sync();
            future.channel().closeFuture().sync();
        } finally {
            //关闭EventLoopGroup,释放所有的资源
            group.shutdownGracefully().sync();
            worker.shutdownGracefully().sync();
        }
    }
}

EchoServerHandler

@ChannelHandler.Sharable //标识一个 ChannelHandler可以被多个Channel安全地共享
public class EchoServerHandler extends ChannelInboundHandlerAdapter {
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        ByteBuf buffer = (ByteBuf) msg;
        //将消息记录到控制台
        System.out.println("Server received: " + buffer.toString(CharsetUtil.UTF_8));
        //将接受到消息回写给发送者
        ctx.write(buffer);
    }

    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
        //将未消息冲刷到远程节点,并且关闭该 Channel
        ctx.writeAndFlush(Unpooled.EMPTY_BUFFER)
                .addListener(ChannelFutureListener.CLOSE);
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        //打印异常栈跟踪
        cause.printStackTrace();
        //关闭该Channel
        ctx.close();
    }
}

Интерпретация кодовой точки:

  • ServerBootStrapЭто класс начальной загрузки, вспомогательный класс, помогающий запустить службу, и он может устанавливать параметры сокета.
  • EventLoopGroupЭто пул потоков, который обрабатывает операции ввода-вывода и используется для распределения операций ввода-вывода и событий, которые обслуживают канал.EventLoopNioEventLoopGroupдаEventLoopGroupКласс реализации . Два экземпляра здесьNioEventLoopGroup,Одинboss, в основном используемый для обработки клиентских подключений,workerИспользуется для обработки операций чтения и записи данных на стороне клиента.
  • EchoServerHandlerРеализована бизнес-логика
  • позвонивServerBootStrap.bind()метод привязки к серверу

Код клиента

public class EchoClient {
    private final String host;
    private final int port;


    public EchoClient(String host, int port) {
        this.host = host;
        this.port = port;
    }

    public void start() throws InterruptedException {
        EventLoopGroup group = new NioEventLoopGroup();
        try {
            Bootstrap b = new Bootstrap();
            b.group(group)
                    .channel(NioSocketChannel.class)
                    .remoteAddress(new InetSocketAddress(host, port))
                    .handler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel socketChannel) throws Exception {
                            socketChannel.pipeline().addLast(new EchoClientHandler());
                        }
                    });
            ChannelFuture channelFuture = b.connect().sync();
            channelFuture.channel().closeFuture().sync();
        } finally {
            group.shutdownGracefully().sync();
        }
    }

    public static void main(String[] args) throws InterruptedException {
        new EchoClient("127.0.0.1", 8888).start();
    }
}

EchoClientHandler

@ChannelHandler.Sharable
public class EchoClientHandler extends SimpleChannelInboundHandler<ByteBuf> {

    @Override
    protected void channelRead0(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf) throws Exception {
        System.out.println("Client received: "+byteBuf.toString());
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        ctx.writeAndFlush(Unpooled.copiedBuffer("Netty rocks",CharsetUtil.UTF_8));
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        cause.printStackTrace();
        ctx.close();
    }
}

Интерпретация кодовой точки:

  • Для инициализации клиента создается экземпляр BootStrap сServerBootStrapТо же самое, это также класс начальной загрузки, в основном помогающий клиенту
  • назначенNioEventLoopGroupэкземпляр, внутриEventLoop, который обрабатывает события, происходящие в течение жизненного цикла соединения.
  • EchoClientHandlerКласс отвечает за обработку бизнес-логики, а серверная частьEchoSeverHandlerЭффект аналогичен.

Ссылки и благодарности