当前位置: 首页 > news >正文

聊聊分布式架构06——[NIO入门]简单的Netty NIO示例

目录

Java NIO和Netty NIO比较

Java NIO:

Netty:

Netty NIO中的主要模块

Transport(传输层)

Buffer(缓冲区)

Codec(编解码器)

Handler(处理器)

EventLoop(事件循环)

Bootstrap和Channel(引导和通道)

Future和Promise(异步编程)

Netty示例

服务端时序图

服务端代码

客户端时序图

客户端代码

总结


Java NIO和Netty NIO比较

Java NIO:
  1. 原生Java库: Java NIO是Java标准库的一部分,提供了非阻塞I/O的核心功能。它是Java平台上的底层API,允许开发者直接操作通道、缓冲区和选择器等组件。

  2. 较低级别: Java NIO是相对较低级别的API,需要开发者编写更多的底层代码来处理网络通信和协议。这可以提供更多的控制权,但也增加了开发复杂性。

  3. 多路复用: Java NIO通过选择器(Selector)实现多路复用,允许一个线程管理多个通道。这对于处理大量并发连接非常有用。

  4. 手动管理缓冲区: Java NIO需要开发者手动管理缓冲区,包括分配、读取、写入和释放缓冲区。这可能导致更复杂的代码结构。

  5. 适用场景: Java NIO适用于需要高度自定义的网络协议网络服务器。它在性能和灵活性方面提供了更多的控制权。

Netty:
  1. 高级别框架: Netty是一个基于Java NIO的高级别框架,它封装了底层的NIO细节,提供了更简单和强大的API来处理网络通信。

  2. 事件驱动: Netty是事件驱动的框架,使用事件处理器(Event Handlers)来处理入站和出站事件。这使得编写网络应用程序更加模块化和可维护。

  3. 自动管理缓冲区: Netty自动管理缓冲区,无需开发者手动分配和释放缓冲区。这减轻了内存管理的负担。

  4. 丰富的功能: Netty提供了丰富的功能,包括HTTP、WebSocket、TLS/SSL支持、UDP通信、拆包和粘包处理等。它还支持异步和同步I/O操作。

  5. 社区和生态系统: Netty拥有强大的社区支持和丰富的生态系统,有大量的扩展和插件可用。这使得开发人员可以更快地构建复杂的网络应用。

  6. 适用场景: Netty适用于构建高性能、可扩展、可维护的网络应用程序,特别是在处理协议复杂、并发连接众多的情况下。

为什么选择Netty?

Java NIONetty NIO
API和类库繁杂麻烦,需掌握Selector、Channel、Buffer等封装简单,门槛低
扩展实现需要熟悉多线程和网络编程保证代码质量通过ChannelHandler对框架灵活扩展
可靠性可靠性能力需手动补齐,工作量和难度大预编码、多协议,功能强,性能高
Bug Fixed臭名昭著的epoll bug(selector空轮询,CPU到100%)1.6版本说修复,1.7版本还在,只是调低了触发率稳定,成熟,修复了所有已发现的Java NIO Bug
社区生态/社区活跃,迭代周期短
生存迭代/经历大规模商业应用考验,质量得到验证

基础篇我们使用Java NIO举例演示了简单的RPC通信。代码的行数和步骤确实挺繁琐,不如来看看Netty 的操作,是骡子是马牵出来溜溜先,就当诸君饭后消个食。

Netty NIO中的主要模块

Netty是一个强大的网络编程框架,它由多个主要模块组成,每个模块负责不同的功能。以下是Netty中的一些主要模块:

  1. Transport(传输层)
    • NIO: 这个模块实现了基于Java NIO的传输层,提供了非阻塞的网络通信功能。它包括NioEventLoopGroupNioServerSocketChannelNioSocketChannel等类,用于创建和管理NIO通道。

  2. Buffer(缓冲区)
    • Java NIO中的ByteBuffer局限性:

      • ByteBuffer长度固定,不能动态伸缩和扩展,编码对象时容易引起索引越界异常

      • ByteBuffer只有一个标识位置的指针position,需要手工调用flip()和rewind(),不方便

      • ByteBuffer的API功能有限,需要使用者自己编程实现一些高级和实用的特性

    • 为了弥补这些不足,Netty NIO提供了自己的实现——ByteBuf:

      Netty提供了高性能的缓冲区实现ByteBuf,用于处理数据的读取和写入。ByteBuf提供了直接和间接缓冲区,并支持池化,以减少内存分配和回收的开销。

  3. Codec(编解码器)
    • 编码器和解码器: 这个模块包括一系列编解码器,用于将数据序列化和反序列化为字节,以便在网络中传输。Netty提供了JSON、Protobuf、HTTP、WebSocket等多种编解码器。

  4. Handler(处理器)
    • ChannelHandler: ChannelHandler是Netty中的核心概念,用于处理事件和数据。它可以自定义,用于构建处理链。Netty提供了各种内置的ChannelHandler,如SimpleChannelInboundHandlerChannelDuplexHandler等。

  5. EventLoop(事件循环)
    • EventLoopGroup: 这个模块包括了EventLoopGroupEventLoop,用于实现事件循环机制。EventLoopGroup管理一组EventLoop,每个EventLoop负责处理一组通道上的事件。事件循环是Netty实现异步和事件驱动的关键。

  6. Bootstrap和Channel(引导和通道)
    • ServerBootstrap和Bootstrap: 这两个类用于引导Netty应用程序的启动。ServerBootstrap用于启动服务器端,而Bootstrap用于启动客户端。

    • Channel和ChannelPipeline: Channel表示通道,它代表了一个网络连接。在Channel接口层,采用Fade模式进行统一封装。ChannelPipeline是处理链,包含一系列ChannelHandler,用于处理事件和数据。

  7. Future和Promise(异步编程)
    • Future: Netty使用Future来表示异步操作的结果,允许开发者异步地等待操作完成。

    • Promise: PromiseFuture的扩展,允许开发者设置操作的结果,使得异步编程更加方便。

Netty示例

服务端时序图

服务端代码
/*** Netty Server* 开始:需要绑定端口用于启动* 1.创建线程组bossGroup用于处理客户端连接* 2.创建线程组workGroup用于socket网络读写* 3.创建Bootstrap服务启动辅助类,类似serverSocketChannel* 4.链式编程,构造线程组、serverChannel、options、channelHandle* 5.ChannelHandle继承自ChannelInitializer<SocketChannel>进行功能扩展* 结束:优雅退出关闭资源*/
public class Server {public static void main(String[] args) {new Server().bind(8088);}
​/*** 绑定端口用于启动* @param port 服务端口*/public void bind(int port) {// 1.创建线程组bossGroup处理客户端连接EventLoopGroup bossGroup = new NioEventLoopGroup();// 2.创建线程组workGroup用于socket网络读写EventLoopGroup workerGroup = new NioEventLoopGroup();try {// 3.创建Bootstrap服务启动辅助类,类似serverSocketChannelServerBootstrap server = new ServerBootstrap();// 4.链式编程,构造线程组、serverChannel、options、channelHandleserver.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class).option(ChannelOption.SO_BACKLOG, 1024).childHandler(new ChildChannelHandler());System.out.println("server start on port:" + port);
​// 绑定端口异步调用连接客户端,同步阻塞等待(连接结果)连接成功ChannelFuture channelFuture = server.bind(port).sync();
​// 异步调用close关闭链路,同步阻塞等待(关闭结果)关闭成功后退出main函数channelFuture.channel().closeFuture().sync();} catch (InterruptedException e) {e.printStackTrace();} finally {// 优雅退出关闭资源bossGroup.shutdownGracefully();workerGroup.shutdownGracefully();}}
​/*** 5.ChannelHandle继承自ChannelInitializer<SocketChannel>进行功能扩展*/private class ChildChannelHandler extends ChannelInitializer<SocketChannel> {
​@Overrideprotected void initChannel(SocketChannel socketChannel) throws Exception {socketChannel.pipeline().addLast(new ServerHandler());}}
​private class ServerHandler extends ChannelHandlerAdapter {@Overridepublic void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {ByteBuf buf = (ByteBuf) msg;byte[] req = new byte[buf.readableBytes()]; // 读取缓冲区可读取长度buf.readBytes(req);String body = new String(req, "UTF-8");System.out.println("Server receive msg:" + body);String currTime = "query time".equalsIgnoreCase(body) ? new Date(System.currentTimeMillis()).toString() : "error code";ByteBuf resp = Unpooled.copiedBuffer(currTime.getBytes());ctx.write(resp);}
​@Overridepublic void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {ctx.close();}
​@Overridepublic void channelReadComplete(ChannelHandlerContext ctx) throws Exception {ctx.flush(); // 将发送缓冲数组中的消息通过flush()写入socketChannel发送,避免了频繁调用selector进行发送}}
}
客户端时序图

客户端代码
/*** 开始:连接服务端ip:port* 1.创建EventLoopGroup管理socket读取* 2.创建客户端辅助类Bootstrap,类似于之前的SocketChannel* 3.链式编程,构造客户端流程client、option、handler* 4.handler使用的是ChannelInitializer<SocketChannel>* 结束:优雅的关闭资源*/
public class Client {public static void main(String[] args) {new Client().connect("localhost", 8088);}
​/*** 连接服务端ip:port* @param ip 服务端地址* @param port 服务端端口*/public void connect(String ip, int port) {// 1.创建EventLoopGroup管理socket读取EventLoopGroup group =new NioEventLoopGroup();try {// 2.创建客户端辅助类Bootstrap,类似于之前的SocketChannelBootstrap client = new Bootstrap();// 3.链式编程,构造客户端流程client、option、handlerclient.group(group).channel(NioSocketChannel.class).option(ChannelOption.TCP_NODELAY, true).handler(new ChannelInitializer<SocketChannel>() {@Overrideprotected void initChannel(SocketChannel socketChannel) throws Exception {socketChannel.pipeline().addLast(new ClientHandler());}});
​// 发起异步连接操作ChannelFuture channelFuture = client.connect(ip, port).sync();
​// 等待服务端链路关闭channelFuture.channel().closeFuture().sync();} catch (InterruptedException e) {e.printStackTrace();} finally {// 优雅退出,释放资源group.shutdownGracefully();}}
​/*** 4.handler使用的是ChannelInitializer<SocketChannel>*/public class ClientHandler extends ChannelHandlerAdapter {@Overridepublic void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {ByteBuf buf = (ByteBuf) msg;byte[] req = new byte[buf.readableBytes()];buf.readBytes(req);String body = new String(req, "UTF-8");System.out.println("client receive server send:" + body);channelActive(ctx);}
​@Overridepublic void channelActive(ChannelHandlerContext ctx) throws Exception {Scanner scanner = new Scanner(System.in);System.out.println("请输入");String clientIn = scanner.nextLine();ByteBuf firstMessage = Unpooled.buffer(clientIn.getBytes().length);firstMessage.writeBytes(clientIn.getBytes());System.out.println("client send:" + clientIn);ctx.writeAndFlush(firstMessage);}@Overridepublic void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {ctx.close();}}
}

运行结果:

总结

不难看出,不管是流程步骤还是实现功能的代码行数,Netty NIO都是优于Java原生NIO的。到此,一个简单的NIO入门示例完成。

资料参考:《Netty权威指南》

相关文章:

聊聊分布式架构06——[NIO入门]简单的Netty NIO示例

目录 Java NIO和Netty NIO比较 Java NIO&#xff1a; Netty&#xff1a; Netty NIO中的主要模块 Transport&#xff08;传输层&#xff09; Buffer&#xff08;缓冲区&#xff09; Codec&#xff08;编解码器&#xff09; Handler&#xff08;处理器&#xff09; Even…...

H5逆向之远程RPC

引言前一讲说过H5 怎么去抓包,逆向分析。其中说到RPC。这一节详细讲一下。有一种情况,JS 比较复杂,混淆的厉害。 这个时候就用到RPC。原理就是,hook web 浏览器,直接调用js 里边的方法。 Node 服务。为什么用到Node 服务,先来看下这架构 Node 对外提供各种接口,外部可以…...

解决Ubuntu18.04安装好搜狗输入法后无法打出中文的问题

首先下载安装 搜狗拼音输入法 &#xff0c;下载选择&#xff1a; x86_64 在ubuntu中设置 fcitx 最后发现安装好了&#xff0c;图标有了 &#xff0c;但是使用时不能输入中文&#xff0c;使用下面的命令解决&#xff1a; sudo apt install libqt5qml5 libqt5quick5 libqt5qu…...

Ubuntu LabelMe AI 识别

1.创建虚拟环境 conda create -n labelme python3.9 2.激活虚拟环境 conda activate labelme 3.安装labelme pip install pyqt5 -i https://pypi.tuna.tsinghua.edu.cn/simple pip install pillow -i https://pypi.tuna.tsinghua.edu.cn/simple pip install labelme -i ht…...

基于FPGA的图像缩小算法实现,包括tb测试文件和MATLAB辅助验证

目录 1.算法运行效果图预览 2.算法运行软件版本 3.部分核心程序 4.算法理论概述 5.算法完整程序工程 1.算法运行效果图预览 将FPGA的处理结果导出到matlab中显示图像效果&#xff1a; 2.算法运行软件版本 vivado2019.2 matlab2022a 3.部分核心程序 timescale 1ns / 1p…...

黑马店评-04缓存更新策略,保证MySQL数据库中的数据和Redis中缓存的数据一致性

缓存更新策略(数据一致) 更新策略 缓存更新是Redis为了节约内存而设计出来的机制,当我们向Redis插入太多数据时就会导致缓存中的数据过多,所以Redis会对部分数据进行更新即淘汰 低一致性需求(数据长久不发生变化): 使用内存淘汰机制,例如店铺类型信息的查询缓存,因为这部分…...

matlab相机标定实验

实验原理 1. 相机标定坐标系 相机的参数对目标的识别、定位精度有很大的影响&#xff0c;相机标定就是为了求出相机的内外参数。标定中有3个不同层次的坐标系&#xff1a;世界坐标系、相机坐标系和图像坐标系&#xff08;图像物理坐标系和图像像素坐标系&#xff09;。世界坐…...

【每日一题Day343】LC2731移动机器人 | 脑筋急转弯+数学

移动机器人【LC2731】 有一些机器人分布在一条无限长的数轴上&#xff0c;他们初始坐标用一个下标从 0 开始的整数数组 nums 表示。当你给机器人下达命令时&#xff0c;它们以每秒钟一单位的速度开始移动。 给你一个字符串 s &#xff0c;每个字符按顺序分别表示每个机器人移动…...

疯狂java 1.7垃圾回收机制

内存泄漏&#xff1a;如果一些分配出去的内存得不到及时回收&#xff0c;就会引起系统运行速度下降&#xff0c;甚至导致程序瘫痪 Java程序的内存分配和回收都是由JRE在后台自动进行的。JRE会负责回收哪些不再使用的内存&#xff0c;这种机制被称为垃圾回收&#xff08;Garbag…...

day01_基础

零、今日内容 1 jdk 2 idea使用 3 HelloWorld程序 4 变量 5 数据类型 6 String 一、JDK安装 JDK java开发工具包,敲代码的环境 1.1 卸载 控制面板 -> 卸载程序 -> 选择jdk,右键卸载 1.2 安装 注意: 现在安装的是JDK8版本,虽然最新的版本是21版本,但是工作市场中最流行的…...

RabbitMQ开启消息发送确认和消费手动确认

开启RabbitMQ的生产者发送消息到RabbitMQ服务端的接收确认&#xff08;ACK&#xff09;和消费者通过手动确认或者丢弃消费的消息。 通过配置 publisher-confirm-type: correlated 和publisher-returns: true开启生产者确认消息。 server:port: 8014spring:rabbitmq:username: …...

嵌入式系统开发【深入浅出】 GPIO 类设备的驱动程序

目录 GPIO管脚的输出功能相当于控制、输入相当于检测 使用GPIO基本流程 对于某一个管脚来说最多有几种功能&#xff1f; 拓展 【定时器与系统定时器】 决定定时长短的因素: 普通定时器 系统定时器 STM32F103RBT6的时钟源有哪五种 sysclk 的时钟频率由哪个时钟源提供基…...

项目管理必备的22个公式

大家好&#xff0c;我是老原。 趁着国庆时间比较空闲&#xff0c;给你们整理了一些项目管理必备的计算公式&#xff0c;一共22个。 每一个公式都给你们标注了适用情况和使用方法&#xff0c;为了方便你们理解&#xff0c;也加了一些例子&#xff0c;保准你看了就会。 觉得不…...

ccache加速编译速度

ccache https://gitee.com/lixiaoxmm/ccache.git 依赖hiredis、zstd(zstd的cmakelists.txt在build/cmake目录下) 下载mingw,https://www.mingw-w64.org/downloads/#w64devkit hiredis、zstd使用mingw编译 cmake -G “MinGW Makefiles” cmakecache.txt手动修改去掉网络下载,…...

Apache POI使用

1.导入坐标 <!-- poi --><dependency><groupId>org.apache.poi</groupId><artifactId>poi</artifactId><version>${poi}</version></dependency><dependency><groupId>org.apache.poi</groupId><a…...

UNIQUE VISION Programming Contest 2023 Autumn(AtCoder Beginner Contest 323)

A - Weak Beats 链接 : A - Weak Beats 思路 : 模拟即可,如果在偶数位上出现了非0得元素&#xff0c;直接输出"No"后返回即可&#xff0c;循环顺利结束的话&#xff0c;就直接输出"Yes"; 代码 : #include<bits/stdc.h> #define IOS ios::sy…...

Docker 网络管理

Docker 网络实现原理 Docker使用Linux桥接&#xff0c;在宿主机虚拟一个Docker容器网桥(docker0)&#xff0c;Docker启动一个容器时会根据Docker网桥的网段分配给容器一个IP地址&#xff0c;称为Container-IP&#xff0c;同时Docker网桥是每个容器的默认网关。因为在同一宿主机…...

网络安全国家队-安防思考与实践

按照工信部“三同步”安全建设的统一要求&#xff0c;本项目的实施应具备符合等级保护要求的安全防护措施&#xff08;主要为传输控制、防火墙隔离、入侵检测、安全审计等网络安全措施&#xff1b;操作系统安全、数据库安全、防病毒管理、安全审计等基础系统安全措施&#xff0…...

epoll 定时器

参考&#xff1a; Linux下使用epoll监听定时器-CSDN博客 但是这个用的是gettimeofday。 本人使用的是 #include <stdlib.h> #include<stdio.h> #include <sys/timerfd.h> #include <sys/epoll.h> #include <unistd.h> #include <sys/time.…...

BUUCTF Java逆向解密 1

Class文件是Java编译后的二进制字节码文件。 我这里使用的是jadx-gui&#xff0c;直接将class文件拖进去即可 package defpackage;import java.util.ArrayList; import java.util.Scanner;/* renamed from: Reverse reason: default package */ /* loaded from: Reverse.clas…...

地震勘探——干扰波识别、井中地震时距曲线特点

目录 干扰波识别反射波地震勘探的干扰波 井中地震时距曲线特点 干扰波识别 有效波&#xff1a;可以用来解决所提出的地质任务的波&#xff1b;干扰波&#xff1a;所有妨碍辨认、追踪有效波的其他波。 地震勘探中&#xff0c;有效波和干扰波是相对的。例如&#xff0c;在反射波…...

SciencePlots——绘制论文中的图片

文章目录 安装一、风格二、1 资源 安装 # 安装最新版 pip install githttps://github.com/garrettj403/SciencePlots.git# 安装稳定版 pip install SciencePlots一、风格 简单好用的深度学习论文绘图专用工具包–Science Plot 二、 1 资源 论文绘图神器来了&#xff1a;一行…...

阿里云ACP云计算备考笔记 (5)——弹性伸缩

目录 第一章 概述 第二章 弹性伸缩简介 1、弹性伸缩 2、垂直伸缩 3、优势 4、应用场景 ① 无规律的业务量波动 ② 有规律的业务量波动 ③ 无明显业务量波动 ④ 混合型业务 ⑤ 消息通知 ⑥ 生命周期挂钩 ⑦ 自定义方式 ⑧ 滚的升级 5、使用限制 第三章 主要定义 …...

定时器任务——若依源码分析

分析util包下面的工具类schedule utils&#xff1a; ScheduleUtils 是若依中用于与 Quartz 框架交互的工具类&#xff0c;封装了定时任务的 创建、更新、暂停、删除等核心逻辑。 createScheduleJob createScheduleJob 用于将任务注册到 Quartz&#xff0c;先构建任务的 JobD…...

(二)原型模式

原型的功能是将一个已经存在的对象作为源目标,其余对象都是通过这个源目标创建。发挥复制的作用就是原型模式的核心思想。 一、源型模式的定义 原型模式是指第二次创建对象可以通过复制已经存在的原型对象来实现,忽略对象创建过程中的其它细节。 📌 核心特点: 避免重复初…...

在Ubuntu中设置开机自动运行(sudo)指令的指南

在Ubuntu系统中&#xff0c;有时需要在系统启动时自动执行某些命令&#xff0c;特别是需要 sudo权限的指令。为了实现这一功能&#xff0c;可以使用多种方法&#xff0c;包括编写Systemd服务、配置 rc.local文件或使用 cron任务计划。本文将详细介绍这些方法&#xff0c;并提供…...

【学习笔记】深入理解Java虚拟机学习笔记——第4章 虚拟机性能监控,故障处理工具

第2章 虚拟机性能监控&#xff0c;故障处理工具 4.1 概述 略 4.2 基础故障处理工具 4.2.1 jps:虚拟机进程状况工具 命令&#xff1a;jps [options] [hostid] 功能&#xff1a;本地虚拟机进程显示进程ID&#xff08;与ps相同&#xff09;&#xff0c;可同时显示主类&#x…...

3-11单元格区域边界定位(End属性)学习笔记

返回一个Range 对象&#xff0c;只读。该对象代表包含源区域的区域上端下端左端右端的最后一个单元格。等同于按键 End 向上键(End(xlUp))、End向下键(End(xlDown))、End向左键(End(xlToLeft)End向右键(End(xlToRight)) 注意&#xff1a;它移动的位置必须是相连的有内容的单元格…...

管理学院权限管理系统开发总结

文章目录 &#x1f393; 管理学院权限管理系统开发总结 - 现代化Web应用实践之路&#x1f4dd; 项目概述&#x1f3d7;️ 技术架构设计后端技术栈前端技术栈 &#x1f4a1; 核心功能特性1. 用户管理模块2. 权限管理系统3. 统计报表功能4. 用户体验优化 &#x1f5c4;️ 数据库设…...

处理vxe-table 表尾数据是单独一个接口,表格tableData数据更新后,需要点击两下,表尾才是正确的

修改bug思路&#xff1a; 分别把 tabledata 和 表尾相关数据 console.log() 发现 更新数据先后顺序不对 settimeout延迟查询表格接口 ——测试可行 升级↑&#xff1a;async await 等接口返回后再开始下一个接口查询 ________________________________________________________…...