Netty 入门
文章目录
- 一、概述
- 1.1 Netty 是什么?
- 1.2 Netty 的地位
- 1.3 Netty 的优势
- 二、Hello World
- 2.1 目标
- 2.2 服务器端
- 2.3 客户端
- 2.4 流程梳理
- 三、组件
- 3.1 EventLoop
- 3.2 演示 NioEventLoop 处理 io 事件
- 3.3 演示 NioEventLoop 处理普通任务
- 3.4 演示 NioEventLoop 处理定时任务
一、概述
1.1 Netty 是什么?
Netty 是一个异步的、基于事件驱动的网络应用框架,用于快速开发可维护、高性能的网络服务器和客户端。
1.2 Netty 的地位
Netty 在 Java 网络应用框架中的地位就好比:Spring 框架在 JavaEE 开发中的地位
以下的框架都使用了 Netty,因为它们有网络通信需求!
- Cassandra - nosql 数据库
- Spark - 大数据分布式计算框架
- Hadoop - 大数据分布式存储框架
- RocketMQ - ali 开源的消息队列
- ElasticSearch - 搜索引擎
- gRPC - rpc 框架
- Dubbo - rpc 框架
- Spring 5.x - flux api 完全抛弃了 tomcat ,使用 netty 作为服务器端
- Zookeeper - 分布式协调框架
1.3 Netty 的优势
- Netty vs NIO,工作量大,bug 多
- 需要自己构建协议
- 解决 TCP 传输问题,如粘包、半包
- epoll 空轮询导致 CPU 100%
- 对 API 进行增强,使之更易用,如 FastThreadLocal => ThreadLocal,ByteBuf => ByteBuffer
- Netty vs 其它网络应用框架
- Mina 由 apache 维护,将来 3.x 版本可能会有较大重构,破坏 API 向下兼容性,Netty 的开发迭代更迅速,API 更简洁、文档更优秀
- 久经考验,16年,Netty 版本
- 2.x 2004
- 3.x 2008
- 4.x 2013
- 5.x 已废弃(没有明显的性能提升,维护成本高)
二、Hello World
2.1 目标
开发一个简单的服务器端和客户端
- 客户端向服务器端发送 hello, world
- 服务器仅接收,不返回
加入依赖
<dependency><groupId>io.netty</groupId><artifactId>netty-all</artifactId><version>4.1.39.Final</version>
</dependency>
2.2 服务器端
new ServerBootstrap().group(new NioEventLoopGroup()) // 1.channel(NioServerSocketChannel.class) // 2.childHandler(new ChannelInitializer<NioSocketChannel>() { // 3protected void initChannel(NioSocketChannel ch) {ch.pipeline().addLast(new StringDecoder()); // 5ch.pipeline().addLast(new SimpleChannelInboundHandler<String>() { // 6@Overrideprotected void channelRead0(ChannelHandlerContext ctx, String msg) {System.out.println(msg);}});}}).bind(8080); // 4
代码解读
-
1 处,创建 NioEventLoopGroup,可以简单理解为
线程池 + Selector后面会详细展开 -
2 处,选择服务 Scoket 实现类,其中 NioServerSocketChannel 表示基于 NIO 的服务器端实现,其它实现还有

-
3 处,为啥方法叫 childHandler,是接下来添加的处理器都是给 SocketChannel 用的,而不是给 ServerSocketChannel。ChannelInitializer 处理器(仅执行一次),它的作用是待客户端 SocketChannel 建立连接后,执行 initChannel 以便添加更多的处理器
-
4 处,ServerSocketChannel 绑定的监听端口
-
5 处,SocketChannel 的处理器,解码 ByteBuf => String
-
6 处,SocketChannel 的业务处理器,使用上一个处理器的处理结果
2.3 客户端
new Bootstrap().group(new NioEventLoopGroup()) // 1.channel(NioSocketChannel.class) // 2.handler(new ChannelInitializer<Channel>() { // 3@Overrideprotected void initChannel(Channel ch) {ch.pipeline().addLast(new StringEncoder()); // 8}}).connect("127.0.0.1", 8080) // 4.sync() // 5.channel() // 6.writeAndFlush(new Date() + ": hello world!"); // 7
代码解读
-
1 处,创建 NioEventLoopGroup,同 Server
-
2 处,选择客户 Socket 实现类,NioSocketChannel 表示基于 NIO 的客户端实现,其它实现还有

-
3 处,添加 SocketChannel 的处理器,ChannelInitializer 处理器(仅执行一次),它的作用是待客户端 SocketChannel 建立连接后,执行 initChannel 以便添加更多的处理器
-
4 处,指定要连接的服务器和端口
-
5 处,Netty 中很多方法都是异步的,如 connect,这时需要使用 sync 方法等待 connect 建立连接完毕
-
6 处,获取 channel 对象,它即为通道抽象,可以进行数据读写操作
-
7 处,写入消息并清空缓冲区
-
8 处,消息会经过通道 handler 处理,这里是将 String => ByteBuf 发出
-
数据经过网络传输,到达服务器端,服务器端 5 和 6 处的 handler 先后被触发,走完一个流程
2.4 流程梳理

💡 提示
一开始需要树立正确的观念
- 把 channel 理解为数据的通道
- 把 msg 理解为流动的数据,最开始输入是 ByteBuf,但经过 pipeline 的加工,会变成其它类型对象,最后输出又变成 ByteBuf
- 把 handler 理解为数据的处理工序
- 工序有多道,合在一起就是 pipeline,pipeline 负责发布事件(读、读取完成…)传播给每个 handler, handler 对自己感兴趣的事件进行处理(重写了相应事件处理方法)
- handler 分 Inbound 和 Outbound 两类
- 把 eventLoop 理解为处理数据的工人
- 工人可以管理多个 channel 的 io 操作,并且一旦工人负责了某个 channel,就要负责到底(绑定)
- 工人既可以执行 io 操作,也可以进行任务处理,每位工人有任务队列,队列里可以堆放多个 channel 的待处理任务,任务分为普通任务、定时任务
- 工人按照 pipeline 顺序,依次按照 handler 的规划(代码)处理数据,可以为每道工序指定不同的工人
三、组件
3.1 EventLoop
事件循环对象
EventLoop 本质是一个单线程执行器(同时维护了一个 Selector),里面有 run 方法处理 Channel 上源源不断的 io 事件。
它的继承关系比较复杂
- 一条线是继承自 j.u.c.ScheduledExecutorService 因此包含了线程池中所有的方法
- 另一条线是继承自 netty 自己的 OrderedEventExecutor,
- 提供了 boolean inEventLoop(Thread thread) 方法判断一个线程是否属于此 EventLoop
- 提供了 parent 方法来看看自己属于哪个 EventLoopGroup
事件循环组
EventLoopGroup 是一组 EventLoop,Channel 一般会调用 EventLoopGroup 的 register 方法来绑定其中一个 EventLoop,后续这个 Channel 上的 io 事件都由此 EventLoop 来处理(保证了 io 事件处理时的线程安全)
- 继承自 netty 自己的 EventExecutorGroup
- 实现了 Iterable 接口提供遍历 EventLoop 的能力
- 另有 next 方法获取集合中下一个 EventLoop
以一个简单的实现为例:
// 内部创建了两个 EventLoop, 每个 EventLoop 维护一个线程
DefaultEventLoopGroup group = new DefaultEventLoopGroup(2);
System.out.println(group.next());
System.out.println(group.next());
System.out.println(group.next());
输出
io.netty.channel.DefaultEventLoop@60f82f98
io.netty.channel.DefaultEventLoop@35f983a6
io.netty.channel.DefaultEventLoop@60f82f98
也可以使用 for 循环
DefaultEventLoopGroup group = new DefaultEventLoopGroup(2);
for (EventExecutor eventLoop : group) {System.out.println(eventLoop);
}
输出
io.netty.channel.DefaultEventLoop@60f82f98
io.netty.channel.DefaultEventLoop@35f983a6
💡 优雅关闭
优雅关闭
shutdownGracefully方法。该方法会首先切换EventLoopGroup到关闭状态从而拒绝新的任务的加入,然后在任务队列的任务都处理完成后,停止线程的运行。从而确保整体应用是在正常有序的状态下退出的
3.2 演示 NioEventLoop 处理 io 事件
服务器端两个 nio worker 工人
new ServerBootstrap().group(new NioEventLoopGroup(1), new NioEventLoopGroup(2)).channel(NioServerSocketChannel.class).childHandler(new ChannelInitializer<NioSocketChannel>() {@Overrideprotected void initChannel(NioSocketChannel ch) {ch.pipeline().addLast(new ChannelInboundHandlerAdapter() {@Overridepublic void channelRead(ChannelHandlerContext ctx, Object msg) {ByteBuf byteBuf = msg instanceof ByteBuf ? ((ByteBuf) msg) : null;if (byteBuf != null) {byte[] buf = new byte[16];ByteBuf len = byteBuf.readBytes(buf, 0, byteBuf.readableBytes());log.debug(new String(buf));}}});}}).bind(8080).sync();
客户端,启动三次,分别修改发送字符串为 zhangsan(第一次),lisi(第二次),wangwu(第三次)
public static void main(String[] args) throws InterruptedException {Channel channel = new Bootstrap().group(new NioEventLoopGroup(1)).handler(new ChannelInitializer<NioSocketChannel>() {@Overrideprotected void initChannel(NioSocketChannel ch) throws Exception {System.out.println("init...");ch.pipeline().addLast(new LoggingHandler(LogLevel.DEBUG));}}).channel(NioSocketChannel.class).connect("localhost", 8080).sync().channel();channel.writeAndFlush(ByteBufAllocator.DEFAULT.buffer().writeBytes("wangwu".getBytes()));Thread.sleep(2000);channel.writeAndFlush(ByteBufAllocator.DEFAULT.buffer().writeBytes("wangwu".getBytes()));
最后输出
22:03:34 [DEBUG] [nioEventLoopGroup-3-1] c.i.o.EventLoopTest - zhangsan
22:03:36 [DEBUG] [nioEventLoopGroup-3-1] c.i.o.EventLoopTest - zhangsan
22:05:36 [DEBUG] [nioEventLoopGroup-3-2] c.i.o.EventLoopTest - lisi
22:05:38 [DEBUG] [nioEventLoopGroup-3-2] c.i.o.EventLoopTest - lisi
22:06:09 [DEBUG] [nioEventLoopGroup-3-1] c.i.o.EventLoopTest - wangwu
22:06:11 [DEBUG] [nioEventLoopGroup-3-1] c.i.o.EventLoopTest - wangwu
可以看到两个工人轮流处理 channel,但工人与 channel 之间进行了绑定

再增加两个非 nio 工人
DefaultEventLoopGroup normalWorkers = new DefaultEventLoopGroup(2);
new ServerBootstrap().group(new NioEventLoopGroup(1), new NioEventLoopGroup(2)).channel(NioServerSocketChannel.class).childHandler(new ChannelInitializer<NioSocketChannel>() {@Overrideprotected void initChannel(NioSocketChannel ch) {ch.pipeline().addLast(new LoggingHandler(LogLevel.DEBUG));ch.pipeline().addLast(normalWorkers,"myhandler",new ChannelInboundHandlerAdapter() {@Overridepublic void channelRead(ChannelHandlerContext ctx, Object msg) {ByteBuf byteBuf = msg instanceof ByteBuf ? ((ByteBuf) msg) : null;if (byteBuf != null) {byte[] buf = new byte[16];ByteBuf len = byteBuf.readBytes(buf, 0, byteBuf.readableBytes());log.debug(new String(buf));}}});}}).bind(8080).sync();
客户端代码不变,启动三次,分别修改发送字符串为 zhangsan(第一次),lisi(第二次),wangwu(第三次)
输出
22:19:48 [DEBUG] [nioEventLoopGroup-4-1] i.n.h.l.LoggingHandler - [id: 0x251562d5, L:/127.0.0.1:8080 - R:/127.0.0.1:52588] REGISTERED
22:19:48 [DEBUG] [nioEventLoopGroup-4-1] i.n.h.l.LoggingHandler - [id: 0x251562d5, L:/127.0.0.1:8080 - R:/127.0.0.1:52588] ACTIVE
22:19:48 [DEBUG] [nioEventLoopGroup-4-1] i.n.h.l.LoggingHandler - [id: 0x251562d5, L:/127.0.0.1:8080 - R:/127.0.0.1:52588] READ: 8B+-------------------------------------------------+| 0 1 2 3 4 5 6 7 8 9 a b c d e f |
+--------+-------------------------------------------------+----------------+
|00000000| 7a 68 61 6e 67 73 61 6e |zhangsan |
+--------+-------------------------------------------------+----------------+
22:19:48 [DEBUG] [nioEventLoopGroup-4-1] i.n.h.l.LoggingHandler - [id: 0x251562d5, L:/127.0.0.1:8080 - R:/127.0.0.1:52588] READ COMPLETE
22:19:48 [DEBUG] [defaultEventLoopGroup-2-1] c.i.o.EventLoopTest - zhangsan
22:19:50 [DEBUG] [nioEventLoopGroup-4-1] i.n.h.l.LoggingHandler - [id: 0x251562d5, L:/127.0.0.1:8080 - R:/127.0.0.1:52588] READ: 8B+-------------------------------------------------+| 0 1 2 3 4 5 6 7 8 9 a b c d e f |
+--------+-------------------------------------------------+----------------+
|00000000| 7a 68 61 6e 67 73 61 6e |zhangsan |
+--------+-------------------------------------------------+----------------+
22:19:50 [DEBUG] [nioEventLoopGroup-4-1] i.n.h.l.LoggingHandler - [id: 0x251562d5, L:/127.0.0.1:8080 - R:/127.0.0.1:52588] READ COMPLETE
22:19:50 [DEBUG] [defaultEventLoopGroup-2-1] c.i.o.EventLoopTest - zhangsan
22:20:24 [DEBUG] [nioEventLoopGroup-4-2] i.n.h.l.LoggingHandler - [id: 0x94b2a840, L:/127.0.0.1:8080 - R:/127.0.0.1:52612] REGISTERED
22:20:24 [DEBUG] [nioEventLoopGroup-4-2] i.n.h.l.LoggingHandler - [id: 0x94b2a840, L:/127.0.0.1:8080 - R:/127.0.0.1:52612] ACTIVE
22:20:25 [DEBUG] [nioEventLoopGroup-4-2] i.n.h.l.LoggingHandler - [id: 0x94b2a840, L:/127.0.0.1:8080 - R:/127.0.0.1:52612] READ: 4B+-------------------------------------------------+| 0 1 2 3 4 5 6 7 8 9 a b c d e f |
+--------+-------------------------------------------------+----------------+
|00000000| 6c 69 73 69 |lisi |
+--------+-------------------------------------------------+----------------+
22:20:25 [DEBUG] [nioEventLoopGroup-4-2] i.n.h.l.LoggingHandler - [id: 0x94b2a840, L:/127.0.0.1:8080 - R:/127.0.0.1:52612] READ COMPLETE
22:20:25 [DEBUG] [defaultEventLoopGroup-2-2] c.i.o.EventLoopTest - lisi
22:20:27 [DEBUG] [nioEventLoopGroup-4-2] i.n.h.l.LoggingHandler - [id: 0x94b2a840, L:/127.0.0.1:8080 - R:/127.0.0.1:52612] READ: 4B+-------------------------------------------------+| 0 1 2 3 4 5 6 7 8 9 a b c d e f |
+--------+-------------------------------------------------+----------------+
|00000000| 6c 69 73 69 |lisi |
+--------+-------------------------------------------------+----------------+
22:20:27 [DEBUG] [nioEventLoopGroup-4-2] i.n.h.l.LoggingHandler - [id: 0x94b2a840, L:/127.0.0.1:8080 - R:/127.0.0.1:52612] READ COMPLETE
22:20:27 [DEBUG] [defaultEventLoopGroup-2-2] c.i.o.EventLoopTest - lisi
22:20:38 [DEBUG] [nioEventLoopGroup-4-1] i.n.h.l.LoggingHandler - [id: 0x79a26af9, L:/127.0.0.1:8080 - R:/127.0.0.1:52625] REGISTERED
22:20:38 [DEBUG] [nioEventLoopGroup-4-1] i.n.h.l.LoggingHandler - [id: 0x79a26af9, L:/127.0.0.1:8080 - R:/127.0.0.1:52625] ACTIVE
22:20:38 [DEBUG] [nioEventLoopGroup-4-1] i.n.h.l.LoggingHandler - [id: 0x79a26af9, L:/127.0.0.1:8080 - R:/127.0.0.1:52625] READ: 6B+-------------------------------------------------+| 0 1 2 3 4 5 6 7 8 9 a b c d e f |
+--------+-------------------------------------------------+----------------+
|00000000| 77 61 6e 67 77 75 |wangwu |
+--------+-------------------------------------------------+----------------+
22:20:38 [DEBUG] [nioEventLoopGroup-4-1] i.n.h.l.LoggingHandler - [id: 0x79a26af9, L:/127.0.0.1:8080 - R:/127.0.0.1:52625] READ COMPLETE
22:20:38 [DEBUG] [defaultEventLoopGroup-2-1] c.i.o.EventLoopTest - wangwu
22:20:40 [DEBUG] [nioEventLoopGroup-4-1] i.n.h.l.LoggingHandler - [id: 0x79a26af9, L:/127.0.0.1:8080 - R:/127.0.0.1:52625] READ: 6B+-------------------------------------------------+| 0 1 2 3 4 5 6 7 8 9 a b c d e f |
+--------+-------------------------------------------------+----------------+
|00000000| 77 61 6e 67 77 75 |wangwu |
+--------+-------------------------------------------------+----------------+
22:20:40 [DEBUG] [nioEventLoopGroup-4-1] i.n.h.l.LoggingHandler - [id: 0x79a26af9, L:/127.0.0.1:8080 - R:/127.0.0.1:52625] READ COMPLETE
22:20:40 [DEBUG] [defaultEventLoopGroup-2-1] c.i.o.EventLoopTest - wangwu
可以看到,nio 工人和 非 nio 工人也分别绑定了 channel(LoggingHandler 由 nio 工人执行,而我们自己的 handler 由非 nio 工人执行)

💡 handler 执行中如何换人?
关键代码io.netty.channel.AbstractChannelHandlerContext#invokeChannelRead()static void invokeChannelRead(final AbstractChannelHandlerContext next, Object msg) {final Object m = next.pipeline.touch(ObjectUtil.checkNotNull(msg, "msg"), next);// 下一个 handler 的事件循环是否与当前的事件循环是同一个线程EventExecutor executor = next.executor();// 是,直接调用if (executor.inEventLoop()) {next.invokeChannelRead(m);} else { // 不是,将要执行的代码作为任务提交给下一个事件循环处理(换人)executor.execute(new Runnable() {@Overridepublic void run() {next.invokeChannelRead(m);}});} }
- 如果两个 handler 绑定的是同一个线程,那么就直接调用
- 否则,把要调用的代码封装为一个任务对象,由下一个 handler 的线程来调用
3.3 演示 NioEventLoop 处理普通任务
3.4 演示 NioEventLoop 处理定时任务
相关文章:
Netty 入门
文章目录一、概述1.1 Netty 是什么?1.2 Netty 的地位1.3 Netty 的优势二、Hello World2.1 目标2.2 服务器端2.3 客户端2.4 流程梳理三、组件3.1 EventLoop3.2 演示 NioEventLoop 处理 io 事件3.3 演示 NioEventLoop 处理普通任务3.4 演示 NioEventLoop 处理定时任务…...
收藏|一文掌握数据分析在企业的实际流程
一、数据分析概念 1.1 数据分析 是指用适当的统计分析方法对收集来的大量数据进行分析,将他们加以汇总和理解并消化,以求最大化地开发数据的功能,发挥数据的作用。 1.2 数据分析包括 描述性数据分析(初级数据分析)…...
100ask_imx6ull 输出PWM
查看PWM对应扩展板的引脚 100ask_imx6ul通过扩展板插槽来验证pwm波,所以这里通过扩展板的原理图及芯片手册可知,gpio4_io20,gpio4_io19分别对应着PWM8和PWM7。 设置设备树 打开官方NXP的工具i.MX pins v6工具,PWM7/PWM8的配置如…...
yolov5编译安卓APP:解决图像上全是检测框
yolov5编译安卓APP:解决图像上全是检测框前言一、第一个YOLOv5 APP1.参考链接2.详细说明3.APP检测时图像上全是框的解决方法二、第二个YOLOv5 APP1.参考链接2.详细说明3.APP检测时图像上全是框的解决方法三、其他1.APK打包2.修改APP图标与名字前言 YOLOv5编译安卓A…...
为什么我们需要地图?
想一想,武侠小说里面。一张藏宝图,引来江湖腥风血雨,要么是武功秘籍,要么是绝世宝剑,要么是富可敌国的财富,只要有了藏宝图,便可曲径通幽,到达彼岸。 由此可见,地图的重…...
攻防世界1.新手练习区
4.攻防世界1.新手练习区 1.view_source 访问url: http://111.200.241.244:48855/ 鼠标点击右键不起作用,F12审查元素 得到flag为cyberpeace{0f3a3e4ab8c8664f3cf40d4240ec7b53} 2.robots 访问url: http://111.200.241.244:34362/ rob…...
Python进阶篇(二)-- Django 深入模型
上一节提到了Django是基于MVC架构的Web框架,MVC架构追求的是“模型”和“视图”的解耦合。所谓“模型”说得更直白一些就是数据(的表示),所以通常也被称作“数据模型”。在实际的项目中,数据模型通常通过数据库实现持久…...
ABAP SALV实现弹出ALV选择
问题场景 需要弹出一个ALV并获取选择的数据 实现思路 跳转屏幕弹出ALV(通过SALV)弹出ALV(通过REUSE_ALV_POPUP_TO_SELECT) 实现效果 因为这里需要的是单选,所以没有多选列 实现代码 MODULE sel_zfretype INPUT.…...
git check-pick,git patch 与 git stash 详解
大家好,我是 17。 今天和大家聊一聊 git check-pick,git patch 与 git stash 的用法。 git cherry-pick 为什么要用 cherry-pick? 不适合 merge 的场景就可以考虑 cherry-pick。 试想下面这些场景 只想同步分支的部分提交。两个分支是两上完全独立…...
OA漏洞-到处搜集整理
一米OA getfile.jsp 任意文件读取漏洞 原文链接 漏洞复现 一米OA getfile.jsp 任意文件读取漏洞 一米OA协同办公系统,集成了OA办公自动化系统、手机客户端、专业报表工具,为全国千万企业用户提供全功能、性价比高的OA软件。一米OA getfile.jsp文件存在任意文件读取漏洞&am…...
web端接收读卡器卡片信息
项目背景 通过电脑连接的读卡器读取卡片信息,并由web页面接收和处理卡片信息。 读卡器抛出卡片信息流程 卡片贴近或放置到读卡器上读卡器解析卡片信息,并形成固定格式的字符串,包括的信息有:卡片写入的数据、卡片原数据&#x…...
BUUCTF-练习场-WEB-第一部分(8道)
[极客大挑战 2019]EasySQL 1payload:1 or 11#是闭合前面的查询语句,or 11恒成立,可以使用or句子绕过判断,#用于注释,注释后面的内容不再执行,所以该sql命令会返回表内所有内容,其实就是实现一个…...
Java Reflection 实战- Class类
Java Reflection 实战 - Class Java 反射使得在运行时检查类、接口、字段和方法成为可能,而不需要在编译时知道类、方法等的名称。也可以使用反射来实例化新对象、调用方法和获取/设置字段值。 Java反射的功能相当强大,可以说是非常有用。例如ÿ…...
背包问题理解思路(01背包、完全背包、分组背包)
这两天把经典的三个背包问题看了一下,网上大多文章是以代码和公式为主,因为平时没刷过算法题所以理解起来花了些时间,固写一篇文章记录理解思路,本文不包含代码实现(理解了思路代码实现应该是小问题,网上一…...
Mr. Cappuccino的第39杯咖啡——Kubernetes之深入理解Pod
Kubernetes之深入理解PodPod相关概念Pod详细配置清单Pod核心配置Pod基本配置1. 创建yaml文件2. 创建namespace并根据yaml文件创建资源3. 查看namespace下的pod列表以及pod的详细信息Pod中多个容器的名称和端口号不能相同Pod镜像拉取策略Pod环境变量Pod端口相关设置Pod资源相关配…...
SqlSession 和 SqlSessionTemplate 简单使用及注意事项
1、SqlSession 简单使用 先简单说下 SqlSession 是什么?SqlSession 是对 Connection 的包装,简化对数据库操作。所以你获取到一个 SqlSession 就相当于获取到一个数据库连接,就可以对数据库进行操作。 SqlSession API 如下图示:…...
1. QSaveFile和QFile的简单使用
1. 说明 QSaveFile和QFile两个类都是用来操作文件的,区别在于QSaveFile在对文件进行写入时有一种保护机制,再写入出错时,不会对源文件中的内容进行操作。该类在执行写操作时,会先将内容写入到一个临时文件中,如果没有…...
工业4.0是如何优化垃圾处理行业的
如今,工业4.0正在影响着制造业和物流等行业,其发展潜力在未来还有望进一步扩大。一些全球领先的垃圾处理公司已经开始在水处理和废物回收等领域应用工业4.0。工业4.0的创新给这个领域带来了一些必要的改进。随着环境危机的加剧,垃圾处理行业面…...
vue 动画(transition)
一、 实现原理 在插入、更新、移除 DOM 元素时,在合适的时候给元素添加样式类名,配合 CSS 样式使用,实现动画效果。 通俗来讲,就是将要进行动画操作的 DOM 元素用 transition 标签包裹起来。在此html元素运动前,运动…...
Python 爬虫工程师面试经验分享,金三银四
🙃 作为一个 Python 爬虫工程师,我可以分享一些我在面试中的经验和建议。 首先一点是在面试中要表现自信、友好、乐于合作,同时对公司的业务和文化也要有一定的了解和兴趣,这些也是公司在招聘中看重的因素。 文章目录🕛…...
docker详细操作--未完待续
docker介绍 docker官网: Docker:加速容器应用程序开发 harbor官网:Harbor - Harbor 中文 使用docker加速器: Docker镜像极速下载服务 - 毫秒镜像 是什么 Docker 是一种开源的容器化平台,用于将应用程序及其依赖项(如库、运行时环…...
Leetcode 3576. Transform Array to All Equal Elements
Leetcode 3576. Transform Array to All Equal Elements 1. 解题思路2. 代码实现 题目链接:3576. Transform Array to All Equal Elements 1. 解题思路 这一题思路上就是分别考察一下是否能将其转化为全1或者全-1数组即可。 至于每一种情况是否可以达到…...
工程地质软件市场:发展现状、趋势与策略建议
一、引言 在工程建设领域,准确把握地质条件是确保项目顺利推进和安全运营的关键。工程地质软件作为处理、分析、模拟和展示工程地质数据的重要工具,正发挥着日益重要的作用。它凭借强大的数据处理能力、三维建模功能、空间分析工具和可视化展示手段&…...
Python如何给视频添加音频和字幕
在Python中,给视频添加音频和字幕可以使用电影文件处理库MoviePy和字幕处理库Subtitles。下面将详细介绍如何使用这些库来实现视频的音频和字幕添加,包括必要的代码示例和详细解释。 环境准备 在开始之前,需要安装以下Python库:…...
推荐 github 项目:GeminiImageApp(图片生成方向,可以做一定的素材)
推荐 github 项目:GeminiImageApp(图片生成方向,可以做一定的素材) 这个项目能干嘛? 使用 gemini 2.0 的 api 和 google 其他的 api 来做衍生处理 简化和优化了文生图和图生图的行为(我的最主要) 并且有一些目标检测和切割(我用不到) 视频和 imagefx 因为没 a…...
LLMs 系列实操科普(1)
写在前面: 本期内容我们继续 Andrej Karpathy 的《How I use LLMs》讲座内容,原视频时长 ~130 分钟,以实操演示主流的一些 LLMs 的使用,由于涉及到实操,实际上并不适合以文字整理,但还是决定尽量整理一份笔…...
Linux nano命令的基本使用
参考资料 GNU nanoを使いこなすnano基础 目录 一. 简介二. 文件打开2.1 普通方式打开文件2.2 只读方式打开文件 三. 文件查看3.1 打开文件时,显示行号3.2 翻页查看 四. 文件编辑4.1 Ctrl K 复制 和 Ctrl U 粘贴4.2 Alt/Esc U 撤回 五. 文件保存与退出5.1 Ctrl …...
C语言中提供的第三方库之哈希表实现
一. 简介 前面一篇文章简单学习了C语言中第三方库(uthash库)提供对哈希表的操作,文章如下: C语言中提供的第三方库uthash常用接口-CSDN博客 本文简单学习一下第三方库 uthash库对哈希表的操作。 二. uthash库哈希表操作示例 u…...
认识CMake并使用CMake构建自己的第一个项目
1.CMake的作用和优势 跨平台支持:CMake支持多种操作系统和编译器,使用同一份构建配置可以在不同的环境中使用 简化配置:通过CMakeLists.txt文件,用户可以定义项目结构、依赖项、编译选项等,无需手动编写复杂的构建脚本…...
【安全篇】金刚不坏之身:整合 Spring Security + JWT 实现无状态认证与授权
摘要 本文是《Spring Boot 实战派》系列的第四篇。我们将直面所有 Web 应用都无法回避的核心问题:安全。文章将详细阐述认证(Authentication) 与授权(Authorization的核心概念,对比传统 Session-Cookie 与现代 JWT(JS…...
