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

高阶面试-netty部分

介绍下netty

Netty 是一个基于 Java 的异步事件驱动的网络应用框架,提供了用于快速开发高性能、高可扩展性的协议服务器和客户端的工具

BIO、NIO、AIO 的区别是什么

BIO

blocking io,同步阻塞IO,比较简单,为每个请求分配一个线程处理,基于stream流

缺点:

  • 性能问题 并发量大,需要创建大量线程,导致系统开销增加,性能下降
  • 资源浪费 每个连接需要一个线程处理,连接空闲时,线程也占用系统资源,浪费资源

因此,jdk1.4引入了NIO

NIO

NIO基于channel和buffer的非阻塞IO

特点:

  • 非阻塞 channel是双向的,可读可写,而传统的stream是单向的;设置非阻塞后,线程在没有数据可读可写时立即返回,而不是阻塞等待
  • 通道和缓冲区 缓冲区提高数据的读写效率,并支持直接内存访问direct buffer,避免JVM内存和系统内存之间的复制
  • 选择器selector,同时监控多个通道的IO事件,使得一个线程可以管理多个通道,减少线程的数量和上下文切换的开销

AIO

NIO的selector在做轮询的时候,如果没有事件发生,也会阻塞,如何优化?

jdk1.7引入AIO,真正异步IO,可以提交读写操作,立刻返回,无需等待,操作完成,操作系统会通知;IO操作完会回调相应的处理器,不需要线程阻塞等待。

netty的应用场景

分布式系统中的 RPC 框架网络通信工具、HTTP 服务器、即时通讯系统、消息推送系统等

你们用在什么场景?
用作服务,比如agi服务,提高通话服务的并发

netty的核心组件

包括 ByteBuf 字节容器、Bootstrap 和 ServerBootstrap 启动引导类、Channel 网络操作抽象类、EventLoop 事件循环等

channel

Netty 中用于网络 I/O 操作的基本构件,类似于传统的 Java NIO 中的 Channel。它代表了一个打开的连接,可以执行读、写、连接和绑定等操作

  • NioSocketChannel:基于 NIO 的 Socket 通道,实现了客户端连接。
  • NioServerSocketChannel:基于 NIO 的服务器 Socket 通道,实现了服务器端的监听。
  • NioDatagramChannel:基于 NIO 的 UDP 通道,用于无连接的数据报传输

EventLoopEventLoopGroup

EventLoop 是一个处理所有 I/O 事件的核心抽象,负责在其生命周期内处理一个或多个 Channel 的 I/O 操作。EventLoopGroup 是一组 EventLoop,用于管理和调度多个 EventLoop

  • NioEventLoopGroup:使用 NIO Selector 实现的 EventLoopGroup
  • EpollEventLoopGroup:使用 epoll 实现的 EventLoopGroup,适用于 Linux 平台。

BootstrapServerBootstrap

Netty 提供的辅助类,用于简化客户端和服务器的启动配置

ChannelFuture

代表一个异步的 I/O 操作结果,提供了检查操作是否完成的方法,并可以注册监听器,在操作完成时得到通知。

ChannelHandlerChannelPipeline

ChannelHandler 是处理 I/O 事件或拦截 I/O 操作的核心组件。ChannelPipeline 是一个处理 ChannelHandler 链的容器,负责将 I/O 事件按顺序传递给链中的各个处理器。

常见的 ChannelHandler

  • ChannelInboundHandler:处理入站 I/O 事件。
  • ChannelOutboundHandler:处理出站 I/O 操作

ByteBuf

ByteBuf 是 Netty 提供的用于数据读写的缓冲区,比 JDK 的 ByteBuffer 更加灵活和高效。它支持动态扩展和各种操作,如切片、复制和聚合

ChannelInitializer

ChannelInitializer 是一个特殊的 ChannelInboundHandler,用于在 Channel 注册到 EventLoop 后初始化 ChannelPipeline

Reactor 线程模型

一种并发编程模型,定义了三种角色:
Reactor:负责监听和分配事件,将I/O事件分派给对应的Handler。新的事件包含连接建立就绪、读就绪、写就绪等。
Acceptor:处理客户端新连接,并分派请求到处理器链中。
Handler:将自身与事件绑定,执行非阻塞读/写任务,完成channel的读入,完成处理业务逻辑后,负责将结果写出channel

三类:
单reactor单线程

![[Pasted image 20240626220034.png]]
单reactor多线程
![[Pasted image 20240626220130.png]]
多reactor多线程:主从reactor,也称为1+M+N 线程模式,被nginx、netty、memcached等使用

MainReactor 只负责监听客户端连接请求,和客户端建立连接之后将连接交由SubReactor 监听后面的 IO 事件
![[Pasted image 20240626220154.png]]

netty的reactor实现

![[Pasted image 20240626221336.png]]

Netty 的高性能体现在哪些方面

1. 异步非阻塞 I/O (NIO)

Netty 基于 Java NIO 库构建,使用异步非阻塞 I/O 模型,有效地利用了系统资源。与传统的阻塞 I/O 模型相比,NIO 可以在同一个线程中处理多个连接,减少了线程切换和上下文切换的开销。

2. 高效的线程模型

Netty 提供了灵活的线程模型,通过事件循环 (EventLoop) 和工作线程池来处理 I/O 事件和任务。默认情况下,Netty 使用主从 Reactor 模型,主线程组处理客户端连接,工作线程组处理读写和业务逻辑。这样的设计避免了线程之间的竞争,提高了性能。

3. 零拷贝 (Zero-Copy)

Netty 使用了多种零拷贝技术来减少数据在内存中的拷贝次数,提高 I/O 效率。例如:

  • FileRegion 用于直接将文件内容传输到网络中。
  • 使用 DirectBuffer 直接进行 I/O 操作,而不需要将数据从用户空间复制到内核空间。

4. 内存管理

Netty 提供了高效的内存管理机制,包括 PooledByteBufAllocatorUnpooledByteBufAllocator。通过池化的方式来分配和管理内存,减少了频繁的内存分配和回收的开销,从而提高了性能。

5. Pipeline 和 Handler 机制

Netty 使用了责任链模式,通过 ChannelPipelineChannelHandler 来处理网络事件。每个 ChannelHandler 只关注自己的处理逻辑,避免了复杂的逻辑集中在一个地方。这样的设计不仅提高了代码的可维护性,还通过流水线方式提升了处理效率。

6. 事件驱动模型

Netty 的事件驱动模型使得它能够高效地处理网络事件。所有的 I/O 操作都是非阻塞的,通过事件通知机制来触发相应的操作,而不是通过轮询的方式。这种方式减少了不必要的系统调用和 CPU 占用。

7. 支持多种协议

Netty 支持多种协议的编解码器,可以方便地处理各种网络协议(如 HTTP, WebSocket, FTP 等)。这些编解码器经过优化,能够高效地进行协议解析和数据处理,减少了开发者自己实现的负担。

8. 高度可定制

Netty 提供了高度可定制的 API,可以根据具体应用的需求进行优化。例如,可以自定义线程池、事件循环、内存分配器等,从而在不同的场景下实现最佳性能。

拆包和粘包

使用 TCP 协议时。它们分别指数据包在传输过程中被拆分成多个小包(拆包)或者多个数据包被合并成一个大包(粘包)的现象

拆包

拆包是指一个完整的数据包在传输过程中被拆分成多个小包。例如,发送端发送了一个较大的数据包,但接收端只能接收到一部分数据,然后再接收剩下的数据。

粘包

粘包是指多个数据包在传输过程中被合并成一个大包。例如,发送端连续发送多个小数据包,但接收端在一次接收操作中接收到多个数据包的数据。

解决方案:

  • 固定长度法

    • 每个数据包的长度是固定的,接收端每次按照固定的长度进行读取。缺点是可能会浪费带宽,因为长度是固定的,无论实际数据量多少都要填充到固定长度。
  • 特殊分隔符法

    • 在数据包之间使用特殊分隔符,接收端通过分隔符来区分数据包。常见的分隔符有换行符 \n、自定义分隔符等。
  • 包头加包体法

    • 在数据包的头部增加一个固定长度的包头,包头中包含数据包的长度信息。接收端首先读取包头,根据包头中的长度信息再读取相应长度的包体
      Netty 提供了多种解码器来解决粘包问题,比如固定长度解码、分隔符解码、长度字段解码等。
  1. 使用固定长度帧解码器 (FixedLengthFrameDecoder)
public void initChannel(SocketChannel ch) throws Exception { ch.pipeline().addLast(new FixedLengthFrameDecoder(20)); // 假设每个消息长度为20 ch.pipeline().addLast(new NettyServerHandler()); }

使用行分隔符解码器 (LineBasedFrameDecoder)

ch.pipeline().addLast(new LineBasedFrameDecoder(1024));// 设定最大帧长度为1024

使用定界符解码器 (DelimiterBasedFrameDecoder)

ch.pipeline().addLast(new DelimiterBasedFrameDecoder(1024, Unpooled.wrappedBuffer(new byte[]{'|'})));

使用基于长度字段的解码器 (LengthFieldBasedFrameDecoder)
在消息头部添加一个长度字段,用于指示消息体的长度

server:
ch.pipeline().addLast(new LengthFieldBasedFrameDecoder(1048576, 0, 4, 0, 4));client:
ch.pipeline().addLast(new LengthFieldPrepender(4));

Netty 的长连接和心跳机制是如何工作

长连接是指客户端和服务器之间建立的连接在一次创建后能够长期保持,Netty 默认使用的就是长连接模式。可以通过设置 ChannelOption.SO_KEEPALIVEtrue 来启用 TCP 层的心跳检测,通常我们还会在应用层实现自己的心跳机制,以确保更细粒度的控制。

实现步骤
  1. 添加心跳处理器
  2. 在服务器端处理心跳请求
  3. 在客户端定时发送心跳请求

server:

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.timeout.IdleStateEvent;
import io.netty.handler.timeout.IdleStateHandler;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;public class HeartbeatServer {public static void main(String[] args) throws InterruptedException {EventLoopGroup bossGroup = new NioEventLoopGroup(1);EventLoopGroup workerGroup = new NioEventLoopGroup();try {ServerBootstrap b = new ServerBootstrap();b.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class).option(ChannelOption.SO_KEEPALIVE, true).handler(new LoggingHandler(LogLevel.INFO)).childHandler(new ChannelInitializer<SocketChannel>() {@Overridepublic void initChannel(SocketChannel ch) throws Exception {ch.pipeline().addLast(new IdleStateHandler(60, 0, 0)); // 60秒内没有读操作则触发IdleStateEventch.pipeline().addLast(new HeartbeatServerHandler());}});b.bind(8080).sync().channel().closeFuture().sync();} finally {bossGroup.shutdownGracefully();workerGroup.shutdownGracefully();}}static class HeartbeatServerHandler extends SimpleChannelInboundHandler<Object> {@Overrideprotected void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception {// 处理其他消息}@Overridepublic void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {if (evt instanceof IdleStateEvent) {IdleStateEvent event = (IdleStateEvent) evt;switch (event.state()) {case READER_IDLE:System.out.println("读超时,关闭连接");ctx.close();break;default:break;}} else {super.userEventTriggered(ctx, evt);}}@Overridepublic void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {cause.printStackTrace();ctx.close();}}
}

client:

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.timeout.IdleStateHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.timeout.IdleStateEvent;
import io.netty.util.concurrent.ScheduledFuture;import java.util.concurrent.TimeUnit;public class HeartbeatClient {public static void main(String[] args) throws InterruptedException {EventLoopGroup group = new NioEventLoopGroup();try {Bootstrap b = new Bootstrap();b.group(group).channel(NioSocketChannel.class).option(ChannelOption.SO_KEEPALIVE, true).handler(new ChannelInitializer<SocketChannel>() {@Overridepublic void initChannel(SocketChannel ch) throws Exception {ch.pipeline().addLast(new IdleStateHandler(0, 30, 0)); // 30秒内没有写操作则触发IdleStateEventch.pipeline().addLast(new HeartbeatClientHandler());}});ChannelFuture f = b.connect("localhost", 8080).sync();f.channel().closeFuture().sync();} finally {group.shutdownGracefully();}}static class HeartbeatClientHandler extends SimpleChannelInboundHandler<Object> {private ScheduledFuture<?> heartBeat;@Overridepublic void channelActive(ChannelHandlerContext ctx) throws Exception {heartBeat = ctx.executor().scheduleAtFixedRate(() -> {ctx.writeAndFlush("HEARTBEAT");System.out.println("发送心跳");}, 0, 30, TimeUnit.SECONDS);}@Overrideprotected void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception {// 处理服务器响应}@Overridepublic void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {if (evt instanceof IdleStateEvent) {IdleStateEvent event = (IdleStateEvent) evt;switch (event.state()) {case WRITER_IDLE:System.out.println("写超时,发送心跳");ctx.writeAndFlush("HEARTBEAT");break;default:break;}} else {super.userEventTriggered(ctx, evt);}}@Overridepublic void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {cause.printStackTrace();ctx.close();}@Overridepublic void channelInactive(ChannelHandlerContext ctx) throws Exception {if (heartBeat != null) {heartBeat.cancel(false);}}}
}

Netty 支持哪些序列化协议

Netty 支持多种序列化协议,用于不同场景下的数据传输需求。以下是一些常见的序列化协议及其在 Netty 中的实现方式:

1. Java 序列化

Java 序列化使用 Java 内置的 ObjectOutputStreamObjectInputStream,可以将 Java 对象转换为字节流。

import io.netty.handler.codec.serialization.ClassResolvers;
import io.netty.handler.codec.serialization.ObjectDecoder;
import io.netty.handler.codec.serialization.ObjectEncoder;// 服务端
ch.pipeline().addLast(new ObjectDecoder(ClassResolvers.cacheDisabled(null)));
ch.pipeline().addLast(new ObjectEncoder());// 客户端
ch.pipeline().addLast(new ObjectDecoder(ClassResolvers.cacheDisabled(null)));
ch.pipeline().addLast(new ObjectEncoder());

2. JSON 序列化

JSON 序列化使用文本格式,易于阅读和调试。可以使用 Jackson 或 Gson 进行 JSON 序列化。

使用 Jackson
import com.fasterxml.jackson.databind.ObjectMapper;
import io.netty.handler.codec.json.JsonObjectDecoder;
import io.netty.handler.codec.MessageToMessageDecoder;
import io.netty.handler.codec.MessageToMessageEncoder;public class JsonEncoder extends MessageToMessageEncoder<Object> {private final ObjectMapper objectMapper = new ObjectMapper();@Overrideprotected void encode(ChannelHandlerContext ctx, Object msg, List<Object> out) throws Exception {byte[] bytes = objectMapper.writeValueAsBytes(msg);out.add(Unpooled.wrappedBuffer(bytes));}
}public class JsonDecoder extends MessageToMessageDecoder<ByteBuf> {private final ObjectMapper objectMapper = new ObjectMapper();private final Class<?> clazz;public JsonDecoder(Class<?> clazz) {this.clazz = clazz;}@Overrideprotected void decode(ChannelHandlerContext ctx, ByteBuf msg, List<Object> out) throws Exception {byte[] bytes = new byte[msg.readableBytes()];msg.readBytes(bytes);Object obj = objectMapper.readValue(bytes, clazz);out.add(obj);}
}// 服务端和客户端
ch.pipeline().addLast(new JsonObjectDecoder());
ch.pipeline().addLast(new JsonDecoder(MyClass.class));
ch.pipeline().addLast(new JsonEncoder());

3. Protobuf 序列化

Protobuf(Protocol Buffers)是 Google 开发的一种高效的二进制序列化协议,适用于高性能场景。

import io.netty.handler.codec.protobuf.ProtobufDecoder;
import io.netty.handler.codec.protobuf.ProtobufEncoder;
import io.netty.handler.codec.protobuf.ProtobufVarint32FrameDecoder;
import io.netty.handler.codec.protobuf.ProtobufVarint32LengthFieldPrepender;// 服务端和客户端
ch.pipeline().addLast(new ProtobufVarint32FrameDecoder());
ch.pipeline().addLast(new ProtobufDecoder(MyProtoClass.getDefaultInstance()));
ch.pipeline().addLast(new ProtobufVarint32LengthFieldPrepender());
ch.pipeline().addLast(new ProtobufEncoder());

5. Kryo 序列化

Kryo 是一个快速、高效的对象图序列化框架,适用于需要高性能序列化的 Java 应用

import com.esotericsoftware.kryo.Kryo;
import com.esotericsoftware.kryo.io.Input;
import com.esotericsoftware.kryo.io.Output;
import io.netty.handler.codec.MessageToByteEncoder;
import io.netty.handler.codec.ByteToMessageDecoder;// 自定义 Kryo 解码器和编码器
public class KryoEncoder extends MessageToByteEncoder<Object> {private final Kryo kryo = new Kryo();@Overrideprotected void encode(ChannelHandlerContext ctx, Object msg, ByteBuf out) throws Exception {ByteArrayOutputStream baos = new ByteArrayOutputStream();Output output = new Output(baos);kryo.writeClassAndObject(output, msg);output.close();out.writeBytes(baos.toByteArray());}
}public class KryoDecoder extends ByteToMessageDecoder {private final Kryo kryo = new Kryo();@Overrideprotected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {byte[] bytes = new byte[in.readableBytes()];in.readBytes(bytes);Input input = new Input(new ByteArrayInputStream(bytes));Object obj = kryo.readClassAndObject(input);out.add(obj);}
}// 服务端和客户端
ch.pipeline().addLast(new KryoEncoder());
ch.pipeline().addLast(new KryoDecoder());

bytebuf相对butebuffer的优点

1. 更丰富的 API

  • 读写索引分离ByteBuf 将读索引和写索引分离,提供了 readerIndexwriterIndex,而 ByteBuffer 只有一个位置指针,通过 flipclear 方法来切换读写模式。
  • 链式调用ByteBuf 的大多数方法都返回 this,允许链式调用,使代码更加简洁和流畅

2. 容量和动态扩展

  • 容量管理ByteBuf 支持动态扩展容量,而 ByteBuffer 的容量是固定的,一旦分配不能改变。
  • 容量检查ByteBuf 提供了多种方法来检查可读字节数和可写字节数,如 readableBytes()writableBytes(),可以避免越界错误。

3. 内存管理

  • 池化机制ByteBuf 支持池化,可以重用缓冲区,减少内存分配和垃圾回收的开销。Netty 的 PooledByteBufAllocator 是一个高效的内存池实现,而 ByteBuffer 只能依赖 JVM 的垃圾回收。
  • 零拷贝ByteBuf 支持零拷贝操作,如 slice()duplicate()composite buffer,减少数据拷贝,提高性能。

4. 引用计数和生命周期管理

  • 引用计数ByteBuf 使用引用计数来管理其生命周期,通过 retain()release() 方法可以精细控制内存的释放。而 ByteBuffer 只能依赖垃圾回收,无法手动管理内存的释放。

5. 更灵活的缓冲区类型

  • 堆内缓冲区和直接缓冲区ByteBuf 支持堆内缓冲区(heap buffer)和直接缓冲区(direct buffer),可以根据需要选择合适的类型。而 ByteBuffer 需要通过 ByteBuffer.allocate()ByteBuffer.allocateDirect() 分别创建。
  • 复合缓冲区ByteBuf 提供了 CompositeByteBuf,允许多个缓冲区组合成一个逻辑缓冲区,避免数据复制。

相关文章:

高阶面试-netty部分

介绍下netty Netty 是一个基于 Java 的异步事件驱动的网络应用框架&#xff0c;提供了用于快速开发高性能、高可扩展性的协议服务器和客户端的工具 BIO、NIO、AIO 的区别是什么 BIO blocking io&#xff0c;同步阻塞IO&#xff0c;比较简单&#xff0c;为每个请求分配一个线…...

在 C++的跨平台开发中,如何处理不同操作系统和编译器之间的细微差异,以确保程序能够稳定且高效地运行?

在 C 的跨平台开发中&#xff0c;处理不同操作系统和编译器之间的细微差异是非常重要的。以下是一些处理差异的技巧&#xff1a; 使用条件编译&#xff1a;使用预处理指令&#xff0c;根据不同的操作系统和编译器来编写不同的代码。 #if defined(_WIN32)// Windows 特定代码 …...

独孤思维:脑子不好使,副业稳赚钱

01 副业&#xff0c;贴身级模仿。 比如独孤最近在测试dy虚拟资料项目。 跑了三个多月。 赚了点下小钱。 从最开始的自动生成视频&#xff0c;到后来的抽帧优化&#xff0c;再到先做的矩阵玩法。 一直都在迭代。 是独孤脑子好使吗&#xff1f; 恰恰相反。 正式因为独孤…...

【数据结构】(C语言):二叉搜索树

二叉搜索树&#xff1a; 树不是线性的&#xff0c;是层级结构。基本单位是节点&#xff0c;每个节点最多2个子节点。有序。每个节点&#xff0c;其左子节点都比它小&#xff0c;其右子节点都比它大。每个子树都是一个二叉搜索树。每个节点及其所有子节点形成子树。可以是空树。…...

泛微开发修炼之旅--23基于ecology自研的数据库分页组件(分页组件支持mysql、sqlserver、oracle、达梦等)

一、使用场景 ecology二开开发过程中&#xff0c;经常要使用到分页查询&#xff0c;随着信创项目的到来&#xff0c;各种国产数据库的出现&#xff0c;对于数据库分页查询兼容何种数据库&#xff0c;就迫在眉睫。 于是&#xff0c;我自己基于ecology开发了一个分页插件&#…...

《昇思25天学习打卡营第4天 | mindspore Transforms 数据变换常见用法》

1. 背景&#xff1a; 使用 mindspore 学习神经网络&#xff0c;打卡第四天&#xff1b; 2. 训练的内容&#xff1a; 使用 mindspore 的常见的数据变换 Transforms 的使用方法&#xff1b; 3. 常见的用法小节&#xff1a; 支持一系列常用的 Transforms 的操作 3.1 Vision …...

【Python时序预测系列】基于LSTM实现多输入多输出单步预测(案例+源码)

这是我的第312篇原创文章。 一、引言 单站点多变量输入多变量输出单步预测问题----基于LSTM实现。 多输入就是输入多个特征变量 多输出就是同时预测出多个标签的结果 单步就是利用过去N天预测未来1天的结果 二、实现过程 2.1 读取数据集 dfpd.read_csv("data.csv&qu…...

git客户端工具之Github,适用于windows和mac

对于我本人&#xff0c;我已经习惯了使用Github Desktop,不同的公司使用的代码管理平台不一样&#xff0c;就好奇Github Desktop是不是也适用于其他平台&#xff0c;结果是可以的。 一、克隆代码 File --> Clone repository… 选择第三种URL方式&#xff0c;输入url &…...

ai除安卓手机版APP软件一键操作自动渲染去擦消稀缺资源下载

安卓手机版&#xff1a;点击下载 苹果手机版&#xff1a;点击下载 电脑版&#xff08;支持Mac和Windows&#xff09;&#xff1a;点击下载 一款全新的AI除安卓手机版APP&#xff0c;一键操作&#xff0c;轻松实现自动渲染和去擦消效果&#xff0c;稀缺资源下载 1、一键操作&…...

Unity获取剪切板内容粘贴板图片文件文字

最近做了一个发送消息的unity项目&#xff0c;需要访问剪切板里面的图片文字文件等&#xff0c;翻遍了网上的东西&#xff0c;看了不是需要导入System.Windows.Forms&#xff08;关键导入了unity还不好用&#xff0c;只能用在纯c#项目中&#xff09;&#xff0c;所以我看了下py…...

利用谷歌云serverless代码托管服务Cloud Functions构建Gemini Pro API

谷歌在2024年4月发布了全新一代的多模态模型Gemini 1.5 Pro&#xff0c;Gemini 1.5 Pro不仅能够生成创意文本和代码&#xff0c;还能理解、总结上传的图片、视频和音频内容&#xff0c;并且支持高达100万tokens的上下文。在多个基准测试中表现优异&#xff0c;性能超越了ChatGP…...

极狐GitLab 17.0 重磅发布,100+ DevSecOps功能更新来啦~【一】

GitLab 是一个全球知名的一体化 DevOps 平台&#xff0c;很多人都通过私有化部署 GitLab 来进行源代码托管。极狐GitLab &#xff1a;https://gitlab.cn/install?channelcontent&utm_sourcecsdn 是 GitLab 在中国的发行版&#xff0c;专门为中国程序员服务。可以一键式部署…...

python实现符文加、解密

在历史悠久的加密技术中&#xff0c;恺撒密码以其简单却有效的原理闻名。通过固定的字母位移&#xff0c;明文可以被转换成密文&#xff0c;而解密则是逆向操作。这种技术不仅适用于英文字母&#xff0c;还可以扩展到其他语言的字符体系&#xff0c;如日语的平假名或汉语的拼音…...

【解释】i.MX6ULL_IO_电气属性说明

【解释】i.MX6ULL_IO_电气属性说明 文章目录 1 Hyst1.1 迟滞&#xff08;Hysteresis&#xff09;是什么&#xff1f;1.2 GPIO的Hyst. Enable Field 参数1.3 应用场景 2 Pull / Keep Select Field2.1 PUE_0_Keeper — Keeper2.2 PUE_1_Pull — Pull2.3 选择Keeper还是Pull 3 Dr…...

02-《石莲》

石 莲 石莲&#xff08;学名&#xff1a;Sinocrassula indica A.Berger&#xff09;&#xff0c;别名因地卡&#xff0c;为二年生草本植物&#xff0c;全株无毛&#xff0c;具须根。花茎高15-60厘米&#xff0c;直立&#xff0c;常被微乳头状突起。茎生叶互生&#xff0c;宽倒披…...

MySQL之聚簇索引和非聚簇索引

1、什么是聚簇索引和非聚簇索引&#xff1f; 聚簇索引&#xff0c;通常也叫聚集索引。 非聚簇索引&#xff0c;指的是二级索引。 下面看一下它们的含义&#xff1a; 1.1、聚集索引选取规则 如果存在主键&#xff0c;主键索引就是聚集索引。如果不存在主键&#xff0c;将使…...

Web后端开发之前后端交互

http协议 http ● 超文本传输协议 &#xff08;HyperText Transfer Protocol&#xff09;服务器传输超文本到本地浏览器的传送协议 是互联网上应用最为流行的一种网络协议,用于定义客户端浏览器和服务器之间交换数据的过程。 HTTP是一个基于TCP/IP通信协议来传递数据. HTT…...

520. 检测大写字母 Easy

我们定义&#xff0c;在以下情况时&#xff0c;单词的大写用法是正确的&#xff1a; 全部字母都是大写&#xff0c;比如 "USA" 。 单词中所有字母都不是大写&#xff0c;比如 "leetcode" 。 如果单词不只含有一个字母&#xff0c;只有首字母大写&#xff0…...

vue的跳转传参

1、接收参数使用route,route包含路由信息&#xff0c;接收参数有两种方式,params和query path跳转只能使用query传参,name跳转都可以 params&#xff1a;获取来自动态路由的参数 query&#xff1a;获取来自search部分的参数 写法 path跳,query传 传参数 import { useRout…...

docker配置镜像源

1&#xff09;打开 docker配置文件 sudo nano /etc/docker/daemon.json 2&#xff09;添加 国内镜像源 {"registry-mirrors": ["https://akchsmlh.mirror.aliyuncs.com","https://registry.docker-cn.com","https://docker.mirrors.ustc…...

MySQL高级-SQL优化-insert优化-批量插入-手动提交事务-主键顺序插入

文章目录 1、批量插入1.1、大批量插入数据1.2、启动Linux中的mysql服务1.3、客户端连接到mysql数据库&#xff0c;加上参数 --local-infile1.4、查询当前会话中 local_infile 系统变量的值。1.5、开启从本地文件加载数据到服务器的功能1.6、创建表 tb_user 结构1.7、上传文件到…...

认识100种电路之振荡电路

在电子电路领域&#xff0c;振荡是一项至关重要的功能。那么&#xff0c;为什么电路中需要振荡&#xff1f;其背后的原理是什么&#xff1f;让我们一同深入探究。 【为什么需要振荡电路】 简单来说&#xff0c;振荡电路的存在是为了产生周期性的信号。在众多电子设备中&#…...

SSH 无密登录配置流程

一、免密登录原理 非对称加密&#xff1a; 由于对称加密的存在弊端&#xff0c;就产生了非对称加密&#xff0c;非对称加密中有两个密钥&#xff1a;公钥和私钥。公钥由私钥产生&#xff0c;但却无法推算出私钥&#xff1b;公钥加密后的密文&#xff0c;只能通过对应的私钥来解…...

Python自动化运维 系统基础信息模块

1.系统信息的收集 系统信息的收集&#xff0c;对于服务质量的把控&#xff0c;服务的监控等来说是非常重要的组成部分&#xff0c;甚至是核心的基础支撑部分。我们可以通过大量的核心指标数据&#xff0c;结合对应的检测体系&#xff0c;快速的发现异常现象的苗头&#xff0c;进…...

如何安装和配置Monit

前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到网站。 关于 Monit Monit 是一个有用的程序&#xff0c;可以自动监控和管理服务器程序&#xff0c;以确保它们不仅保持在线&#xff0c;而且文…...

【redis】redis分片集群基础知识

1、基本概念 1.1定义 分片&#xff1a;数据按照某种规则&#xff08;比如哈希&#xff09;被分割成多个片段&#xff08;或分片&#xff09;&#xff0c;每个片段被称为一个槽&#xff08;slot&#xff09;。槽是Redis分片集群中数据的基本单元。节点&#xff1a;Redis分片集…...

Python 面试【★★★★】

欢迎莅临我的博客 &#x1f49d;&#x1f49d;&#x1f49d;&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:「stormsha的主页」…...

Knife4j 2.2.X 版本 swagger彻底禁用

官方文档配置权限&#xff1a;https://doc.xiaominfo.com/v2/documentation/accessControl.html#_3-5-1-%E7%94%9F%E4%BA%A7%E7%8E%AF%E5%A2%83%E5%B1%8F%E8%94%BD%E8%B5%84%E6%BA%90 通常有时候我们碰到的问题如下&#xff1a; 在开发Knife4j功能时,同很多开发者经常讨论的问…...

linux下mysql的定时备份

备份是容灾的基础&#xff0c;是指为了防止系统出现操作或系统故障导致数据丢失&#xff0c;而将全部或部分数据集合从应用主机的硬盘或阵列复制到其他的存储介质的过程为什么备份 硬件故障软件故障误操作病毒入侵保留历史记录灾难性事件 存储介质 光盘磁带硬盘磁盘阵列DAS:直接…...

【13】地址-比特币区块链的地址

1. 比特币区块链的地址 这就是一个真实的比特币地址:1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa。这是史上第一个比特币地址,据说属于中本聪。 比特币地址是完全公开的,如果你想要给某个人发送币,只需要知道他的地址就可以了。实际上,所谓的地址,只不过是将公钥表示成人类可读…...

【数据结构】数据结构前置知识

这里写目录标题 基本概念与术语数据数据元素数据项数据对象数据结构 逻辑结构和物理结构物理结构顺序存储结构链式存储结构 逻辑结构集合结构线性结构树形结构图形结构 算法时间复杂度和空间复杂度大O的渐进表示法时间复杂度常数阶线性阶对数阶平方阶常见时间复杂度 空间复杂度…...

企业数据挖掘平台产品特色及合作案例介绍

泰迪企业数据挖掘平台是一款通用的、企业级、智能化的数据分析模型构建与数据应用场景设计工具&#xff0c;能够一体化地完成数据集成、模型构建、模型发布&#xff0c;为数据分析、探索、服务流程提供支撑&#xff0c;提供完整的数据探索、多数据源接入、特征处理、模型搭建、…...

C++初学者指南-3.自定义类型(第一部分)-基本自定义类型/类

C初学者指南-3.自定义类型(第一部分)-基本自定义类型/类 文章目录 C初学者指南-3.自定义类型(第一部分)-基本自定义类型/类1.类型种类&#xff08;简化&#xff09;2.为什么选择自定义类型&#xff1f;单向计数器提升序列 3.限制成员访问成员函数公共(public) vs. 私有(private…...

iOS之如何创建.framework静态库

番外&#xff1a;想要查看如何创建.a静态库可前往看我iOS之如何创建.a静态库-CSDN博客这篇文章。 一、创建framework项目 创建framework工程要选择iOS --> Cocoa Touch Framework输入项目名称PrintFramework也是编译生成的framework的名称。framework的名称也可以以后在项目…...

C程序设计谭浩强第五版

程序习题 第一章1、第5题2、第6题 第三章1、第2题2、第2题3、第3题4、第4题Tips 第一章 1、第5题 编写一个C程序,运行时输出以下图形: #include <stdio.h> int main() {for (int i 0; i < 4; i) // 输出4行循环控制{for (int j 0; j < i; j) //第几行就输出几…...

石油化工厂为什么要用专业防爆手机?

防爆手机之所以必须使用专业设计的产品&#xff0c;主要是出于安全考虑&#xff0c;以防止在易燃易爆环境中因手机使用不当引发爆炸事故。以下几点详细解释了使用专业化工防爆手机的必要性&#xff1a; 本质安全设计&#xff1a;顶坚专业防爆手机采用了本质安全&#xff08;本安…...

文本生成sql模型(PipableAI/pip-sql-1.3b)

安装环境 pip3 install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu118 pip install transformers 代码 question "What are the email address, town and county of the customers who are of the least common gender?"sc…...

机器学习中的数学底蕴与设计模式

在说机器学习设计模式之前&#xff0c;想多说几句&#xff0c;在进入软件行业最初的10年&#xff0c;那时候耳熟能详的基本就是多线程编程&#xff0c;互斥同步锁&#xff0c;设计模式&#xff0c;OOA&#xff0c;OOP&#xff0c;常规数组&#xff0c;tree&#xff0c;图的数据…...

【Android面试八股文】性能优化相关面试题:如何查找CPU占用?

文章目录 一、 如何查找CPU的占用问题二、TraceView的使用关于TraceView和Android Studio的Profiler第一步、通过Android studio 打开`Android profiler`第二步、使用步骤第三步、技术说明第四步、CPU占用相关指标说明扩展阅读一、 如何查找CPU的占用问题 在Android开发中,如…...

面试框架一些小结

springcloud的⼯作原理 springcloud由以下⼏个核⼼组件构成&#xff1a; Eureka&#xff1a;各个服务启动时&#xff0c;Eureka Client都会将服务注册到Eureka Server&#xff0c;并且Eureka Client还可以反过来从Eureka Server拉取注册表&#xff0c; 从⽽知道其他服务在哪⾥ …...

c# 往window注册表写入数据后,未写入指定的路径

c# 往window注册表写入数据后&#xff0c;未写入指定的路径 最近在用c#开发一个往注册表写入数据的一个项目&#xff0c;发现将输入写入 HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\CommandStore\shell这个路径时&#xff0c;数据并没写入到这个…...

树莓派4B_OpenCv学习笔记13:OpenCv颜色追踪_程序手动调试HSV色彩空间_检测圆

今日继续学习树莓派4B 4G&#xff1a;&#xff08;Raspberry Pi&#xff0c;简称RPi或RasPi&#xff09; 本人所用树莓派4B 装载的系统与版本如下: 版本可用命令 (lsb_release -a) 查询: Opencv 版本是4.5.1&#xff1a; OpenCv颜色追踪_程序手动调试HSV色彩空间_检测灰度图中的…...

Golang | Leetcode Golang题解之第198题打家劫舍

题目&#xff1a; 题解&#xff1a; func rob(nums []int) int {if len(nums) 0 {return 0}if len(nums) 1 {return nums[0]}first : nums[0]second : max(nums[0], nums[1])for i : 2; i < len(nums); i {first, second second, max(first nums[i], second)}return se…...

基于ruoyi-app的手机短信登录(uniapp)

本篇用于记录h5的框架搭建 组件地址:短信验证码登陆&#xff0c;手机号&#xff0c;验证码倒计时 - DCloud 插件市场 调整后的表单组件代码: <template><view class"login-view"><!-- <input type"tel" confirm-type"确认"…...

机器学习环境搭建

前言 个人笔记&#xff0c;记录框架和小问题&#xff0c;没有太详细记载。。 1、Anaconda安装 下载地址&#xff1a; Free Download | Anaconda &#xff08;慢&#xff09; ​ 国内镜像&#xff1a;https://link.csdn.net/?targethttp%3A%2F%2Fitcxy.xyz%2F241.html 下载…...

2095.删除链表的中间节点

给你一个链表的头节点 head 。删除链表的中间节点 &#xff0c;并返回修改后的链表的头节点 head。 长度为 n 链表的中间节点是从头数起第 ⌊n / 2⌋ 个节点&#xff08;下标从 0 开始&#xff09;&#xff0c;其中 ⌊x⌋ 表示小于或等于 x 的最大整数。 对于 n 1、2、3、4 和…...

Qt QML 坑

Qt QML 坑 QML Listview 1、不定高item 导致item重叠 ListView {id: _cityListViewproperty var _cityArray: [{ type:"A",cityArray:[]},{ type:"B",cityArray:[]},{ type:"C",cityArray:[]},{ type:"D",cityArray:[]}]model: List…...

Chrome浏览器web调试(js调试、css调试、篡改前置)

目录 1. 打开开发者工具(Dev Tool) 2. 打开命令菜单 截图 3. 面板介绍 4. CSS调试 右键检查快速到达元素处 查找DOM数 利用面板Console查找DOM节点 内置函数查找上一个选择点击的元素 5. 调试JS代码(Javascript调试) 日志调试 选择查看日志等级 眼睛观测变量 …...

【Java】Logbook优化接口调用日志输出,优雅!

logbook 简介 很多人可能没有接触过 logbook&#xff0c;但它的确是一个很好用的日志框架。引用官网的介绍 Logbook 是一个可扩展的 Java 库&#xff0c;可以为不同的客户端和服务器端技术启用完整的请求和响应日志记录。它通过以下方式满足了特殊需求&#xff1a; 允许 Web 应…...

LabVIEW电压电流实时监测系统

开发了一种基于LabVIEW和研华&#xff08;Advantech&#xff09;数据采集卡的电压电流实时监测系统&#xff0c;通过高效的数据采集和处理&#xff0c;为工业和科研用户提供高精度、实时的电压电流监测解决方案。系统采用研华USB-4711A数据采集卡&#xff0c;结合LabVIEW编程环…...

线程池666666

1. 作用 线程池内部维护了多个工作线程&#xff0c;每个工作线程都会去任务队列中拿取任务并执行&#xff0c;当执行完一个任务后不是马上销毁&#xff0c;而是继续保留执行其它任务。显然&#xff0c;线程池提高了多线程的复用率&#xff0c;减少了创建和销毁线程的时间。 2…...

项目运维工具——Jpom

Jpom项目运维 最近发现了一个比 Jenkins 使用更简单的项目构建和部署工具,完全可以满足个人以及一些小企业的需求,分享一下。 用了Jpom之后,项目打包方便多了。(真香⚠️ 1介绍 Jpom 是一款 Java 编写的前后端分离项目,并且默认使用 H2 数据库安装此软件非常简单,但前…...

Python基础001

Python输出语句 print输出字符串 print("中国四大名著&#xff1a;","西游记|","三国演义|","红楼梦|","水浒传") print(6) print(1 1)Python输入语句 input函数 input() input("我的名字是&#xff1a;") p…...

JavaSEJava8 时间日期API + 使用心得

文章目录 1. LocalDate2. LocalTime3. LocalDateTime3.1创建 LocalDateTime3.2 LocalDateTime获取方法 4. LocalDateTime转换方法4.1 LocalDateTime增加或者减少时间的方法4.2 LocalDateTime修改方法 5. Period6. Duration7. 格式转换7.1 时间日期转换为字符串7.2 字符串转换为…...

k8s自动补全工具和UI管理界面

分享两个有利于K8S的工具 目录 分享两个有利于K8S的工具 一、部署Dashboard&#xff08;主节点&#xff09; 介绍 1.1、查看集群状态 1.2、下载yaml文件并运行Dashboard 1.3、部署服务 1.4、创建访问账户、获取token&#xff08;令牌&#xff09; 1.5、浏览器访问Dash…...

探囊取物之多形式注册页面(基于BootStrap4)

基于BootStrap4的注册页面&#xff0c;支持手机验证码注册、账号密码注册 低配置云服务器&#xff0c;首次加载速度较慢&#xff0c;请耐心等候&#xff1b;演练页面可点击查看源码 预览页面&#xff1a;http://www.daelui.com/#/tigerlair/saas/preview/ly4gax38ub9j 演练页…...

将生活与出行融合揽境凭什么可以做到?

中国消费者对SUV的钟爱与热衷,堪称市场中的一股强大潮流。他们对其的认可,不仅仅停留在功能性的满足,更是对品质、品味与生活态度的追求。SUV所代表的宽敞空间、卓越通过性和引人注目的外观,恰恰迎合了中国消费者对于舒适、实用与时尚并重的需求。从整个SUV市场来看,30万级…...

领克07EM-P,一款以豪华舒适为核心理念的汽车,好车值得买

领克07EM-P,一款以豪华舒适为核心理念的汽车,将百万豪车的享受带入了每一个驾驶者的生活。领克07不仅拥有豪华的座椅,真皮包装,还具有加热、通风、按摩等功能,让你在驾驶的过程中享受到无与伦比的舒适感。首先,领克07EM-P的座椅设计充满了人性化的考量。豪华的真皮包装,…...

sdbusplus:通过文件描述符传递数据

有的时候需要传递大量的数据,如果将数据通过dbus传递,会消耗大量的带宽。可以通过传递一个文件描述符替代传递数据: 以下的service通过文件描述符接收数据: //fd_service.cpp #include <sdbusplus/asio/connection.hpp> #include <sdbusplus/asio/object_server…...

数据结构 | 详解二叉树——堆与堆排序

&#x1f95d;堆 堆总是一棵完全二叉树。 大堆&#xff1a;父节点总是大于子节点。 小堆&#xff1a;父节点总是小于子节点。 注意&#xff1a;1.同一个节点下的两个子节点并无要求先后顺序。 2.堆可以是无序的。 &#x1f349;堆的实现 &#x1f334;深度剖析 1.父节点和子…...

于AI对话 --如何更好的使用AI工具

文章目录 于AI对话 --如何更好的使用AI工具1、认识AI工具&#xff1a;2、对话原则&#xff1a;3、提问步骤&#xff1a;4、AI可以学习什么&#xff1f;5、提问技巧&#xff1a;1、提出假设性问题:2、&#xff08;鼓励引导式提问&#xff09;跨学科思考:举个例子&#xff1a; 3、…...

构建 Terraform 模块的分步指南

文章目录 一、介绍二、了解 Terraform 模块的基础知识三、Terraform 模块剖析第 1 步&#xff1a;确定可重用组件第 2 步&#xff1a;创建模块结构第 3 步&#xff1a;定义模块的变量第 4 步&#xff1a;编写模块配置第 5 步&#xff1a;提供输出值第 6 步&#xff1a;在配置中…...