Netty 入门实例

文章目录

  • 1. 概述
  • 2. 代码实例
    • 2.1 服务端
    • 2.2 客户端
    • 2.3 运行截图
  • 3. 整体结构
  • 4. 重要组件
    • 4.1 EventLoopGroup、EventLoop
    • 4.2 Handler & Pipeline
    • 4.3 ByteBuf
  • 参考文献

1. 概述

Netty 是一款用于高效开发网络应用的 NIO 网络框架,它大大简化了网络应用的开发过程。

Netty 相比 JDK NIO 的优势:

● 易用性:Netty 在 NIO 基础上进行了更高层次的封装,屏蔽了 NIO 的复杂性,大大降低了开发者的上手难度;
● 稳定性:Netty 修复和完善了 JDK NIO 较多已知问题,例:select 空转导致 CPU 消耗 100%,TCP 断线重连,keep-alive 检测等;
● 可扩展性: 可定制化的线程模型,用户可以通过启动的配置参数选择 Reactor 线程模型;另一个是可扩展的事件驱动模型,将框架层和业务层的关注点分离,开发者只需要关注 ChannelHandler

Netty 比 JDK NIO 更低的资源消耗:

● 对象池复用技术。 Netty 通过复用对象,避免频繁创建和销毁带来的开销;
● 零拷贝技术。 除了操作系统级别的零拷贝技术外,Netty 提供了更多面向用户态的零拷贝技术,例如 Netty 在 I/O 读写时直接使用 DirectBuffer,从而避免了数据在堆内存和堆外内存之间的拷贝;

2. 代码实例

2.1 服务端


import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;

import java.nio.charset.CharacterCodingException;
import java.nio.charset.StandardCharsets;

/**
 * Netty服务端示例01
 */
public class NettyServer01 {
    /**
     * 主函数,服务器的入口点
     * @param args 命令行参数
     */
    public static void main(String[] args) {
        // 创建BossGroup和WorkerGroup,分别处理连接接受和数据读写
        NioEventLoopGroup bossEventLoopGroup = new NioEventLoopGroup(2);
        NioEventLoopGroup workerEventLoopGroup = new NioEventLoopGroup(8);

        new ServerBootstrap() // 初始化ServerBootstrap
                .group(bossEventLoopGroup, workerEventLoopGroup) // 设置EventLoopGroup
                .channel(NioServerSocketChannel.class) // 指定服务器通道类
                .childHandler(new ChannelInitializer<NioSocketChannel>() { // 设置通道初始化器
                    /**
                     * 初始化通道,添加处理器到通道的管道中
                     * @param ch 当前初始化的通道
                     */
                    protected void initChannel(NioSocketChannel ch) {
                        // 添加多个处理器,分别处理入站和出站事件
                        ch.pipeline().addLast(new ChannelInboundHandlerAdapter() {
                            /**
                             * 处理入站数据
                             * @param ctx 通道上下文
                             * @param msg 接收到的消息对象
                             */
                            @Override
                            public void channelRead(ChannelHandlerContext ctx, Object msg) {
                                ByteBuf byteBuf = inbound((ByteBuf) msg, "1");
                                ctx.fireChannelRead(byteBuf);
                            }
                        });
                        ch.pipeline().addLast(new ChannelInboundHandlerAdapter() {
                            @Override
                            public void channelRead(ChannelHandlerContext ctx, Object msg) throws CharacterCodingException {
                                ByteBuf byteBuf = inbound((ByteBuf) msg, "2");
                                ctx.fireChannelRead(byteBuf);
                            }
                        });
                        ch.pipeline().addLast(new ChannelInboundHandlerAdapter() {
                            /**
                             * 处理入站数据,将处理后的数据写回通道
                             * @param ctx 通道上下文
                             * @param msg 接收到的消息对象
                             */
                            @Override
                            public void channelRead(ChannelHandlerContext ctx, Object msg) {
                                ByteBuf byteBuf = inbound((ByteBuf) msg, "3");
                                ctx.channel().write(byteBuf);
                            }
                        });
                        ch.pipeline().addLast(new ChannelOutboundHandlerAdapter() {
                            /**
                             * 处理出站数据,在数据写出前进行加工
                             * @param ctx 通道上下文
                             * @param msg 要写出的消息对象
                             * @param promise 写操作的承诺
                             */
                            @Override
                            public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) {
                                ByteBuf byteBuf = outbound((ByteBuf) msg, "4");
                                ctx.writeAndFlush(msg);
                                ctx.write(byteBuf, promise);
                            }
                        });
                        ch.pipeline().addLast(new ChannelOutboundHandlerAdapter() {
                            @Override
                            public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) {
                                ByteBuf byteBuf = outbound((ByteBuf) msg, "5");
                                ctx.write(byteBuf, promise);
                            }
                        });
                        ch.pipeline().addLast(new ChannelOutboundHandlerAdapter() {
                            @Override
                            public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) {
                                ByteBuf byteBuf = outbound((ByteBuf) msg, "6");
                                ctx.write(byteBuf, promise);
                            }
                        });
                    }
                })
                .bind(8080); // 绑定端口并启动服务器
    }

    /**
     * 对出站数据进行处理
     * @param msg 待处理的ByteBuf对象
     * @param no 数据标识号
     * @return 处理后的ByteBuf对象
     */
    private static ByteBuf outbound(ByteBuf msg, String no) {
        ByteBuf byteBuf = msg;
        String output = byteBufToString(byteBuf);
        System.out.printf("\n\noutbound%s output: %s", no, output);
        stringWriteToByteBuf(byteBuf, String.format("\noutbound%s 已处理", no));
        return byteBuf;
    }

    /**
     * 对入站数据进行处理
     * @param msg 待处理的ByteBuf对象
     * @param no 数据标识号
     * @return 处理后的ByteBuf对象
     */
    private static ByteBuf inbound(ByteBuf msg, String no) {
        String input = byteBufToString(msg);
        System.out.printf("\n\ninbound%s input: %s\n", no, input);
        stringWriteToByteBuf(msg, String.format("\ninbound%s 已处理", no));
        return msg;
    }

    /**
     * 将ByteBuf对象转换为字符串
     * @param msg 待转换的ByteBuf对象
     * @return 字符串表示的数据
     */
    private static String byteBufToString(ByteBuf msg) {
        return msg.toString(StandardCharsets.UTF_8);
    }

    /**
     * 将字符串写入ByteBuf对象
     * @param byteBuf 待写入的ByteBuf对象
     * @param msg 要写入的字符串数据
     */
    private static void stringWriteToByteBuf(ByteBuf byteBuf, String msg) {
        byteBuf.writeBytes(msg.getBytes(StandardCharsets.UTF_8));
    }
}

2.2 客户端

import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.string.StringEncoder;

import java.nio.charset.StandardCharsets;

public class NettyClient01 {
    public static void main(String[] args) {
        new Bootstrap()
                .group(new NioEventLoopGroup())
                .channel(NioSocketChannel.class)
                .handler(new ChannelInitializer<Channel>() {
                    @Override
                    protected void initChannel(Channel ch) {
                        ch.pipeline().addLast(new StringEncoder());
                        ch.pipeline().addLast(new ChannelInboundHandlerAdapter() {
                            @Override
                            public void channelRead(ChannelHandlerContext ctx, Object msg) {
                                System.out.println(((ByteBuf) msg).toString(StandardCharsets.UTF_8));
                            }
                        });
                    }
                })
                .connect("127.0.0.1", 8080)
                .addListener((ChannelFutureListener) future -> {
                    future.channel().writeAndFlush("hello,world");
                });
    }
}

2.3 运行截图

服务端截图:

在这里插入图片描述

客户端截图:
在这里插入图片描述

3. 整体结构

● Boss EventLoopGroup: 负责监听网络连接事件,把新网络连接的Channel 注册到 Worker EventLoopGroup
● Worker EventLoopGroup: 分配一个 EventLoop 负责处理该 Channel 的读写事件,每个 EventLoop 都是单线程的,所以该连接线程安全的,通过 Selector 进行事件循环
● 客户端发起 I/O 读写事件时,服务端 EventLoop 会进行数据的读取,然后通过 Pipeline 触发各种监听器进行数据的加工处理。客户端数据会被传递到 ChannelPipeline 的第一个 ChannelInboundHandler 中,数据处理完成后,将加工完成的数据传递给下一个 ChannelInboundHandler。当数据写回客户端时,会将处理结果在 ChannelPipeline 的 ChannelOutboundHandler 中传播,最后到达客户端。

在这里插入图片描述

4. 重要组件

4.1 EventLoopGroup、EventLoop

EventLoop本质上是一个单线程执行器,维护了一个 Selector,里面有run方法处理Channel上源源不断的io事件。EventLoop继承了ScheduledExecutorService、和自己的OrderedEventExecutor,OrderedEventExecutor 提供了inEventLoop(java.lang.Thread thread)、EventExecutorGroup parent()、EventExecutor next() 等方法。

EventLoopGroup 是一组 EventLoop,Channel一般会调用 EventLoopGroup 的 register 方法绑定其中一个EventLoop,后续这个Channel上 的io都由这个EventLoop处理,EventLoop又是单线程的,保证了单个Channel io 事件处理的线程安全性。

4.2 Handler & Pipeline

ChannelHandler 用来处理 Channel 上的各种事件,分为入站、出站两种。所有 ChannelHandler 被连成一串,就是 Pipeline
● 入站处理器通常是 ChannelInboundHandlerAdapter 的子类,主要用来读取客户端数据,写回结果
● 出站处理器通常是 ChannelOutboundHandlerAdapter 的子类,主要对写回结果进行加工

ChannelInboundHandlerAdapter 是按照 addLast 的顺序执行的,而 ChannelOutboundHandlerAdapter 是按照 addLast 的逆序执行的。ChannelPipeline 的实现是一个 ChannelHandlerContext(包装了 ChannelHandler) 组成的双向链表

4.3 ByteBuf

传送

参考文献

  • 黑马 Netty教程
  • 拉钩教育 Netty 核心原理剖析与 RPC 实践 若地老师
    在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/734501.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【物联网】室内定位技术及定位方式简介

目录 一、概述 二、常用的室内定位技术 2.1 WIFI技术 2.2 UWB超宽带 2.3 蓝牙BLE 2.4 ZigBee技术 2.5 RFID技术 三、常用的室内定位方式 3.1 信号到达时间 3.2 信号到达时间差 3.3 信号到达角 3.4 接收信号强度 一、概述 GPS是目前应用最广泛的定位技术&#xff0…

流行的app抓包工具详解和案例--开发人员首选

个人名片 &#x1f393;作者简介&#xff1a;java领域优质创作者 &#x1f310;个人主页&#xff1a;码农阿豪 &#x1f4de;工作室&#xff1a;新空间代码工作室&#xff08;提供各种软件服务&#xff09; &#x1f48c;个人邮箱&#xff1a;[2435024119qq.com] &#x1f4f1…

游戏大厂“脱钩”安卓商店: 独立渠道TapTap们能否渔利

一纸公告将游戏厂商与渠道的博弈再度摆上了台面。 近日&#xff0c;腾讯控股旗下手游《地下城与勇士&#xff1a;起源》&#xff08;下称“DNF手游”&#xff09;运营团队发布公告称&#xff0c;自6月20日起&#xff0c;DNF手游将不再上架部分安卓平台的头部应用商店。 下架的…

Unity踩坑记录

1. 如果同时在父物体和子物体上挂载BoxCollider&#xff0c;那么当使用&#xff1a; private void OnTriggerEnter2D(Collider2D collision){if (collision.CompareTag("CardGroup")){_intersectCardGroups.Add(collision.GetComponent<CardGroup>());}} 来判…

WebStorm 环境配置带@符号的相对路径穿透

在使用WebStorm 环境开发web页面项目时有时想快速查看页面的引用代码&#xff0c;只能手工找到引入文件路径&#xff0c;这很不方便&#xff0c;只需通过配置webStorm单击打开。 1 使用符号相对路径&#xff0c;在默认情况下没有配置环境是无法打开&#xff0c;如下图&#xf…

docker内apt-get update Waiting for headers 0%

问题描述 docker运行debian等容器时&#xff0c;执行apt update或者apt-get update&#xff0c;可能会出现以下错误&#xff1a;root754a91d3630a:/# apt-get update 0% [Waiting for headers] [Waiting for headers] [Connected to developer.download.nvidia.com (152.199.3…

yolov8环境搭建+训练自己数据集

一、yolov8环境搭建 1. 安装miniconda环境 地址&#xff1a;https://mirrors.tuna.tsinghua.edu.cn/anaconda/miniconda 选择Python3.8版本 最好安装在C盘 勾选自动添加环境变量 ***以下操作安装过程中关闭代理软件 *** 2. 创建虚拟环境 conda create -n yolov8 python3…

Axios-入门

介绍 Axios对原生Ajax进行了封装&#xff0c;简化书写&#xff0c;快速开发 官网&#xff1a;Axios中文文档 | Axios中文网 (axios-http.cn) 入门 1引入Axios的js文件 <script src"js/axios.js"></script> 2使用Axios发送请求&#xff0c;并获取响应…

代理的匿名级别有哪些?为什么匿名性很重要?

在互联网时代&#xff0c;代理服务器在许多领域都扮演着重要的角色。无论是个人用户还是企业&#xff0c;都可能需要使用代理来保护自己的隐私和数据安全。在选择代理服务时&#xff0c;匿名级别是一个重要的考虑因素。本文将介绍代理的匿名级别&#xff0c;并解释为什么匿名性…

PDF转成清晰长图

打开一个宝藏网址在线PDF转换器/处理工具 - 在线工具系列 点击图下所示位置 按照图下所示先上传文件&#xff0c;设置转换参数后点击转换&#xff0c;等待 等待转换完成后&#xff0c;可以在转换结果处选择下载地址&#xff0c;点击即可进行下载使用了。对比了其他几个网站的转…

K8S -理解StatefulSet - 部署有状态应用

什么是 有状态服务和 无状态服务 有状态服务&#xff08;Stateful Service&#xff09;&#xff1a; 有状态服务是指在处理请求期间维护和跟踪用户状态或会话信息的服务。这意味着服务在多个请求之间保持状态&#xff0c;并且需要在请求之间共享和使用这些状态信息。通常&…

网络安全-如何设计一个安全的API(安全角度)

目录 API安全概述设计一个安全的API一个基本的API主要代码调用API的一些问题 BasicAuth认证流程主要代码问题 API Key流程主要代码问题 Bearer auth/Token auth流程 Digest Auth流程主要代码问题 JWT Token流程代码问题 Hmac流程主要代码问题 OAuth比较自定义请求签名身份认证&…

Spring Boot集成Minio插件快速入门

1 Minio介绍 MinIO 是一个基于 Apache License v2.0 开源协议的对象存储服务。它兼容亚马逊 S3 云存储服务接口&#xff0c;非常适合于存储大容量非结构化的数据&#xff0c;例如图片、视频、日志文件、备份数据和容器/虚拟机镜像等&#xff0c;而一个对象文件可以是任意大小&…

Vue38 安装脚手架 vue-cli ,并使用脚手架创建项目

安装脚手架 vue-cli &#xff0c;并使用脚手架创建项目 第一步 安装脚手架 npm config set registry https:\\[registry.npmmirror.com // 切换淘宝镜像 npm install -g vue/cli第二步 切换到创建项目的目录&#xff0c;创建项目 cd XXX vue create XXX第三步 启动项目 npm…

FreeCAD中智能指针分析

实现原理 FreeCAD中有两套智能指针&#xff0c;一个是OCC的智能指针handle&#xff0c;另一个是自己定义的智能指针Reference&#xff0c;两种智能指针都是通过引用计数方式管理指针。 1.1 OCC智能指针handle OCC在基础类包中定义了一个模板类handle&#xff0c;该类包含一个私…

购买服务器,并安装宝塔

前言&#xff1a; 我们在开发项目时&#xff0c;总会遇到一个问题&#xff0c;就是将我们开发好的项目上传的公网中。对于中小型的项目&#xff0c;我们可以通过购买服务器进行项目的上线。 我们的项目一般是部署在Linux环境中。如果你不是专业的运维人员&#xff0c;可能对于…

力扣1901.寻找峰值II

力扣1901.寻找峰值II 二分每一行 并用函数找出每一行中最大值的下标若最大值比其下面相邻的元素大 则上方一定存在峰值若最大值比其下面相邻的元素小 则下方一定存在峰值 class Solution {int indexmax(vector<int> &a){return max_element(a.begin(),a.end()) - …

解决js打开新页面百度网盘显示不存在方法:啊哦,你所访问的页面不存在了。

用js打开新页面open或window.location.href打开百度网盘后都显示&#xff1a;啊哦&#xff0c;你所访问的页面不存在了。 window.open(baidu_url); window.location.href baidu_url;在浏览器上&#xff0c;回车后网盘资源是可以打开的&#xff0c;刷新也是打开的。这是很奇怪…

C/C++ vector模拟实现

模拟实现&#xff1a; 框架 namespace yx {template<class T>class vector{public:typedef T* iterator;private:iterator _start;iterator _finish;iterator _end_of_storage;}; } 这里我们声明定义不分离 reverse() 新开一个空间&#xff0c;拷贝数据&#xff0c;然…

ardupilot开发 --- RTSP视频流 篇

我年轻时很穷&#xff0c;努力了几年&#xff0c;终于不再年轻了 0. 一些概念1. Ubuntu搭建RTSP服务器的方式2. 在Ubuntu上搭建RTSP服务器3. 推流4. 拉流、播放5. 借鉴的一些例子6. 其他参考文献 0. 一些概念 RTSP服务、RTSP推流、RTSP拉流&#xff0c;缺一不可&#xff0c;尤其…