【Netty】Netty 程序引导类(九)
文章目录
- 前言
- 一、引导程序类
- 二、AbstractBootStrap 抽象类
- 三、Bootstrap 类
- 四、ServerBootstrap 类
- 五、引导服务器
- 5.1、 实例化引导程序类
- 5.2、设置 EventLoopGroup
- 5.3、指定 Channel 类型
- 5.4、指定 ChannelHandler
- 5.5、设置 Channel 选项
- 5.6、绑定端口启动服务
- 六、引导客户端
- 6.1、实例化引导程序类
- 6.2、设置 EventLoopGroup
- 6.3、指定 Channel 类型
- 6.4、设置 Channel 选项
- 6.5、指定 ChannelHandler
- 6.6、连接到服务器
- 总结
前言
回顾Netty系列文章:
- Netty 概述(一)
- Netty 架构设计(二)
- Netty Channel 概述(三)
- Netty ChannelHandler(四)
- ChannelPipeline源码分析(五)
- 字节缓冲区 ByteBuf (六)(上)
- 字节缓冲区 ByteBuf(七)(下)
- Netty 如何实现零拷贝(八)
程序引导类(Bootstrap)可以理解为是一个程序的入口程序,在 Java 程序中,就是一个包含 main 方法的程序类。在 Netty 中,引导程序还包含一系列的配置项。本篇文章我们就来介绍 Netty 的引导程序。
一、引导程序类
引导程序类是一种引导程序,使 Netty 程序可以很容易地引导一个 Channel。在 Netty 中,承担引导程序的是 AbstractBootStrap抽象类。
引导程序类都在io.netty.bootstrap包下。AbstractBootStrap抽象类有两个子类:Bootstrap和ServerBootstrap,分别用于引导客户端程序及服务的程序。
下图展示了引导程序类的关系:
从上图可以看出,AbstractBootStrap抽象类实现了Cloneable接口。那么为什么需要实现Cloneable接口呢?
二、AbstractBootStrap 抽象类
在 Netty 中经常需要创建多个具有类似配置或者完全相同配置的Channel。为了支持这种模式而又不避免为每个Channel都创建并配置一个新的引导类实例,因此AbstractBootStrap被标记为了Cloneable。在一个已经配置完成的引导实例上调用clone()方法将返回另一个可以立即使用的引导类实例。
这种方式只会创建引导类实例的EventLoopGroup的一个浅拷贝,所以,EventLoopGroup将在所有克隆的Channel实例之间共享。这是可以接受的,毕竟这些克隆的Channel的生命周期都很短暂,例如,一个典型的场景是创建一个Channel以进行一次HTTP请求。
以下是AbstractBootStrap的核心源码:
public abstract class AbstractBootstrap<B extends AbstractBootstrap<B, C>, C extends Channel> implements Cloneable {volatile EventLoopGroup group;private volatile ChannelFactory<? extends C> channelFactory;private volatile SocketAddress localAddress;private final Map<ChannelOption<?>, Object> options = new LinkedHashMap();private final Map<AttributeKey<?>, Object> attrs = new ConcurrentHashMap();private volatile ChannelHandler handler;AbstractBootstrap() {//禁止从其他程序包扩展}AbstractBootstrap(AbstractBootstrap<B, C> bootstrap) {this.group = bootstrap.group;this.channelFactory = bootstrap.channelFactory;this.handler = bootstrap.handler;this.localAddress = bootstrap.localAddress;synchronized(bootstrap.options) {this.options.putAll(bootstrap.options);}this.attrs.putAll(bootstrap.attrs);}//...
}
从上述源码可以看出,子类型B是其父类型的一个类型参数,因此可以返回到运行时实例的引用以支持方法的链式调用。从私有变量可以看出,AbstractBootStrap所要管理的启动配置包括EventLoopGroup、SocketAddress、Channel配置、ChannelHandler等信息。
AbstractBootStrap是禁止被除io.netty.bootstrap包外其他程序所扩展的,因此可以看到AbstractBootStrap默认构造方法被设置为了包内可见。
三、Bootstrap 类
BootStrap 类是AbstractBootStrap抽象类的子类之一,主要是用于客户端或者使用了无连接协议的应用程序。
以下是BootStrap 类的核心源码:
public class Bootstrap extends AbstractBootstrap<Bootstrap, Channel> {private static final InternalLogger logger = InternalLoggerFactory.getInstance(Bootstrap.class);private static final AddressResolverGroup<?> DEFAULT_RESOLVER;private final BootstrapConfig config = new BootstrapConfig(this);private volatile AddressResolverGroup<SocketAddress> resolver;private volatile SocketAddress remoteAddress;public Bootstrap() {this.resolver = DEFAULT_RESOLVER;}private Bootstrap(Bootstrap bootstrap) {super(bootstrap);this.resolver = DEFAULT_RESOLVER;this.resolver = bootstrap.resolver;this.remoteAddress = bootstrap.remoteAddress;}public Bootstrap resolver(AddressResolverGroup<?> resolver) {this.resolver = resolver == null ? DEFAULT_RESOLVER : resolver;return this;}public Bootstrap remoteAddress(SocketAddress remoteAddress) {this.remoteAddress = remoteAddress;return this;}public Bootstrap remoteAddress(String inetHost, int inetPort) {this.remoteAddress = InetSocketAddress.createUnresolved(inetHost, inetPort);return this;}public Bootstrap remoteAddress(InetAddress inetHost, int inetPort) {this.remoteAddress = new InetSocketAddress(inetHost, inetPort);return this;}public ChannelFuture connect() {this.validate();SocketAddress remoteAddress = this.remoteAddress;if (remoteAddress == null) {throw new IllegalStateException("remoteAddress not set");} else {return this.doResolveAndConnect(remoteAddress, this.config.localAddress());}}public ChannelFuture connect(String inetHost, int inetPort) {return this.connect(InetSocketAddress.createUnresolved(inetHost, inetPort));}public ChannelFuture connect(InetAddress inetHost, int inetPort) {return this.connect(new InetSocketAddress(inetHost, inetPort));}public ChannelFuture connect(SocketAddress remoteAddress) {ObjectUtil.checkNotNull(remoteAddress, "remoteAddress");this.validate();return this.doResolveAndConnect(remoteAddress, this.config.localAddress());}public ChannelFuture connect(SocketAddress remoteAddress, SocketAddress localAddress) {ObjectUtil.checkNotNull(remoteAddress, "remoteAddress");this.validate();return this.doResolveAndConnect(remoteAddress, localAddress);}//...public Bootstrap validate() {super.validate();if (this.config.handler() == null) {throw new IllegalStateException("handler not set");} else {return this;}}public Bootstrap clone() {return new Bootstrap(this);}public Bootstrap clone(EventLoopGroup group) {Bootstrap bs = new Bootstrap(this);bs.group = group;return bs;}public final BootstrapConfig config() {return this.config;}final SocketAddress remoteAddress() {return this.remoteAddress;}final AddressResolverGroup<?> resolver() {return this.resolver;}...
}
上述方法主要分为以下几类:
- group:设置用于处理Channel所有事件的EventLoopGroup。
- channel:指定了 Channel的实现类。
- localAddress:指定 Channel应该绑定到的本地地址。如果没有指定,则有操作系统创建一个随机的地址。或者,也可以通过bind()或者connect()方法指定localAddress。
- option:设置ChannelOption,其将被应用到每个新创建的Channel的ChannelConfig。这些选项将会通过bind()或者connect()方法设置到Channel,配置的顺序与调用先后没有关系。这个方法在Channel已经被创建后再次调用就不会再起任何效果了。支持什么样的ChannelOption取决于所使用的Channel类型。
- attr:指定新创建的Channel的属性值。这些属性值是通过bind()或者connect()方法设置到Channel。配置的顺序取决于调用的先后顺序。这个方法在Channel已经被创建后再次调用就不会再起任何效果了。
- handler:设置被添加到ChannelPipeline以接收事件通知的ChannelHandler。
- remoteAddress:设置远程地址。也可以通过connect()方法来指定它。
clone:创建一个当前Bootstrap的克隆,其具有和原始的Bootstrap相同的设置信息。 - connect:用于连接到远程节点并返回一个ChannelFuture,并将会在连接操作完成后接收到通知。
- bind:绑定Channel并返回一个ChannelFuture,其将会在绑定操作完成之后接收到通知,在那之后必须调用Channel。
BootStrap类中许多方法都继承自AbstractBootstrap类。
四、ServerBootstrap 类
ServerBootstrap 类是AbstractBootStrap抽象类的子类之一,主要是用于引导服务器程序。
以下是 ServerBootstrap 类的源码:
public class ServerBootstrap extends AbstractBootstrap<ServerBootstrap, ServerChannel> {private static final InternalLogger logger = InternalLoggerFactory.getInstance(ServerBootstrap.class);private final Map<ChannelOption<?>, Object> childOptions = new LinkedHashMap();private final Map<AttributeKey<?>, Object> childAttrs = new ConcurrentHashMap();private final ServerBootstrapConfig config = new ServerBootstrapConfig(this);private volatile EventLoopGroup childGroup;private volatile ChannelHandler childHandler;public ServerBootstrap() {}private ServerBootstrap(ServerBootstrap bootstrap) {super(bootstrap);this.childGroup = bootstrap.childGroup;this.childHandler = bootstrap.childHandler;synchronized(bootstrap.childOptions) {this.childOptions.putAll(bootstrap.childOptions);}this.childAttrs.putAll(bootstrap.childAttrs);}public ServerBootstrap group(EventLoopGroup group) {return this.group(group, group);}public ServerBootstrap group(EventLoopGroup parentGroup, EventLoopGroup childGroup) {super.group(parentGroup);if (this.childGroup != null) {throw new IllegalStateException("childGroup set already");} else {this.childGroup = (EventLoopGroup)ObjectUtil.checkNotNull(childGroup, "childGroup");return this;}}public <T> ServerBootstrap childOption(ChannelOption<T> childOption, T value) {ObjectUtil.checkNotNull(childOption, "childOption");synchronized(this.childOptions) {if (value == null) {this.childOptions.remove(childOption);} else {this.childOptions.put(childOption, value);}return this;}}public <T> ServerBootstrap childAttr(AttributeKey<T> childKey, T value) {ObjectUtil.checkNotNull(childKey, "childKey");if (value == null) {this.childAttrs.remove(childKey);} else {this.childAttrs.put(childKey, value);}return this;}public ServerBootstrap childHandler(ChannelHandler childHandler) {this.childHandler = (ChannelHandler)ObjectUtil.checkNotNull(childHandler, "childHandler");return this;}//...public ServerBootstrap clone() {return new ServerBootstrap(this);}/** @deprecated */@Deprecatedpublic EventLoopGroup childGroup() {return this.childGroup;}final ChannelHandler childHandler() {return this.childHandler;}final Map<ChannelOption<?>, Object> childOptions() {synchronized(this.childOptions) {return copiedMap(this.childOptions);}}final Map<AttributeKey<?>, Object> childAttrs() {return copiedMap(this.childAttrs);}public final ServerBootstrapConfig config() {return this.config;}//...
}
上述方法主要分为以下几类:
- group:设置ServerBootstrap要用的EventLoopGroup。这个EventLoopGroup将用于ServerChannel和被接受的子Channel的 I/O 处理。
- channel:设置将要被实例化的ServerChannel类。
- localAddress:指定 ServerChannel应该绑定到的本地地址。如果没有指定,则有操作系统创建一个随机的地址。或者,也可以通过bind()方法指定localAddress。
- option:指定要应用到新创建的ServerChannel的ChannelConfig的ChannelOption。这些选项将会通过bind()设置到Channel,在bind()方法被调用之后,设置或者改变ChannelOption将不会有任何效果。支持什么样的ChannelOption取决于所使用的Channel类型。
- childOption:指定当子Channel被接受时,应用到子Channel的ChannelConfig的ChannelOption。所支持的ChannelOption取决于所使用的Channel类型。
- attr:指定ServerChannel上的属性值。这些属性值是通过bind()或者connect()方法设置到Channel。在bind()方法被调用之后它们将不会有任何效果。
- childAttr:将属性设置给已经被接受的子Channel。之后再次调用将不会有任何效果。
- handler:设置被添加到ServerChannel的ChannelPipeline中的ChannelHandler。
- childHandler:设置将被添加到已被接受的子Channel的ChannelPipeline中的ChannelHandler。
- clone:克隆一个设置好原始的ServerBootstrap相同的ServerBootstrap。
- bind:绑定ServerChannel并返回一个ChannelFuture,其将会在绑定操作完成之后接收到通知(带着成功或者失败的结果)。
ServerBootstrap类中许多方法都继承自AbstractBootstrap类。
五、引导服务器
为了能更好的理解引导程序,下面就以 Echo 协议的服务器的代码为例。核心代码如下:
// 多线程事件循环器
EventLoopGroup bossGroup = new NioEventLoopGroup(); // boss
EventLoopGroup workerGroup = new NioEventLoopGroup(); // workertry {// 启动NIO服务的引导程序类ServerBootstrap b = new ServerBootstrap(); b.group(bossGroup, workerGroup) // 设置EventLoopGroup.channel(NioServerSocketChannel.class) // 指明新的Channel的类型.childHandler(new EchoServerHandler()) // 指定ChannelHandler.option(ChannelOption.SO_BACKLOG, 128) // 设置的ServerChannel的一些选项.childOption(ChannelOption.SO_KEEPALIVE, true); // 设置的ServerChannel的子Channel的选项// 绑定端口,开始接收进来的连接ChannelFuture f = b.bind(port).sync(); System.out.println("EchoServer已启动,端口:" + port);// 等待服务器 socket 关闭 。// 在这个例子中,这不会发生,但你可以优雅地关闭你的服务器。f.channel().closeFuture().sync();
} finally {// 优雅的关闭workerGroup.shutdownGracefully();bossGroup.shutdownGracefully();
}
引导 Netty 服务器主要分为几下几个步骤。
5.1、 实例化引导程序类
在上述代码中,首先是需要实例化引导程序类。由于是服务器端的程序,所以,实例化了一个ServerBootstrap。
5.2、设置 EventLoopGroup
设置ServerBootstrap的EventLoopGroup。上述服务器使用了两个NioEventLoopGroup,一个代表boss线程组,一个代表work线程组。
boss线程主要是接收客户端的请求,并将请求转发给work线程处理。
boss线程是轻量的,不会处理耗时的任务,因此可以承受高并发的请求。而真实的 I/O 操作都是由work线程在执行。
NioEventLoopGroup是支持多线程的,因此可以执行线程池的大小。如果没有指定,则 Netty 会指定一个默认的线程池大小,核心代码如下:
private static final int DEFAULT_EVENT_LOOP_THREADS;static {// 默认 EventLoopGroup 的线程数DEFAULT_EVENT_LOOP_THREADS = Math.max(1, SystemPropertyUtil.getInt("io.netty.eventLoopThreads", NettyRuntime.availableProcessors() * 2));if (logger.isDebugEnabled()) {logger.debug("-Dio.netty.eventLoopThreads: {}", DEFAULT_EVENT_LOOP_THREADS);}}public NioEventLoopGroup() {// 如果不指定, nThreads = 0this(0);}/*** @see MultithreadEventExecutorGroup#MultithreadEventExecutorGroup(int, Executor, Object...)*/protected MultithreadEventLoopGroup(int nThreads, Executor executor, Object... args) {// 如果不指定(nThreads = 0),默认值是 DEFAULT_EVENT_LOOP_THREADSsuper(nThreads == 0 ? DEFAULT_EVENT_LOOP_THREADS : nThreads, executor, args);}
从上述源码可以看出,如果NioEventLoopGroup实例化时,没有指定线程数,则最终默认值是DEFAULT_EVENT_LOOP_THREADS,而默认值DEFAULT_EVENT_LOOP_THREADS是根据当前机子的CPU 处理的个数乘以 2 得出的。
5.3、指定 Channel 类型
channel()方法用于指定ServerBootstrap的Channel类型。在本例中,使用的是NioServerSocketChannel类型,代表了服务器是一个基于ServerSocketChannel的实现,使用基于 NIO 选择器的实现来接受新连接。
5.4、指定 ChannelHandler
childHandler用于指定ChannelHandler,以便处理Channel的请求。上述例子中,指定的是自定义的EchoServerHandler。
5.5、设置 Channel 选项
option和childOption方法,分别用于设置ServerChannel及ServerChannel的子Channel的选项。这些选项定义在ChannelOption类中,包含以下常量:
public class ChannelOption<T> extends AbstractConstant<ChannelOption<T>> {public static final ChannelOption<ByteBufAllocator> ALLOCATOR = valueOf("ALLOCATOR");public static final ChannelOption<RecvByteBufAllocator> RCVBUF_ALLOCATOR = valueOf("RCVBUF_ALLOCATOR");public static final ChannelOption<MessageSizeEstimator> MESSAGE_SIZE_ESTIMATOR = valueOf("MESSAGE_SIZE_ESTIMATOR");public static final ChannelOption<Integer> CONNECT_TIMEOUT_MILLIS = valueOf("CONNECT_TIMEOUT_MILLIS");/*** @deprecated Use {@link MaxMessagesRecvByteBufAllocator}* and {@link MaxMessagesRecvByteBufAllocator#maxMessagesPerRead(int)}.*/@Deprecatedpublic static final ChannelOption<Integer> MAX_MESSAGES_PER_READ = valueOf("MAX_MESSAGES_PER_READ");public static final ChannelOption<Integer> WRITE_SPIN_COUNT = valueOf("WRITE_SPIN_COUNT");/*** @deprecated Use {@link #WRITE_BUFFER_WATER_MARK}*/@Deprecatedpublic static final ChannelOption<Integer> WRITE_BUFFER_HIGH_WATER_MARK = valueOf("WRITE_BUFFER_HIGH_WATER_MARK");/*** @deprecated Use {@link #WRITE_BUFFER_WATER_MARK}*/@Deprecatedpublic static final ChannelOption<Integer> WRITE_BUFFER_LOW_WATER_MARK = valueOf("WRITE_BUFFER_LOW_WATER_MARK");public static final ChannelOption<WriteBufferWaterMark> WRITE_BUFFER_WATER_MARK =valueOf("WRITE_BUFFER_WATER_MARK");public static final ChannelOption<Boolean> ALLOW_HALF_CLOSURE = valueOf("ALLOW_HALF_CLOSURE");public static final ChannelOption<Boolean> AUTO_READ = valueOf("AUTO_READ");/*** If {@code true} then the {@link Channel} is closed automatically and immediately on write failure.* The default value is {@code true}.*/public static final ChannelOption<Boolean> AUTO_CLOSE = valueOf("AUTO_CLOSE");public static final ChannelOption<Boolean> SO_BROADCAST = valueOf("SO_BROADCAST");public static final ChannelOption<Boolean> SO_KEEPALIVE = valueOf("SO_KEEPALIVE");public static final ChannelOption<Integer> SO_SNDBUF = valueOf("SO_SNDBUF");public static final ChannelOption<Integer> SO_RCVBUF = valueOf("SO_RCVBUF");public static final ChannelOption<Boolean> SO_REUSEADDR = valueOf("SO_REUSEADDR");public static final ChannelOption<Integer> SO_LINGER = valueOf("SO_LINGER");public static final ChannelOption<Integer> SO_BACKLOG = valueOf("SO_BACKLOG");public static final ChannelOption<Integer> SO_TIMEOUT = valueOf("SO_TIMEOUT");public static final ChannelOption<Integer> IP_TOS = valueOf("IP_TOS");public static final ChannelOption<InetAddress> IP_MULTICAST_ADDR = valueOf("IP_MULTICAST_ADDR");public static final ChannelOption<NetworkInterface> IP_MULTICAST_IF = valueOf("IP_MULTICAST_IF");public static final ChannelOption<Integer> IP_MULTICAST_TTL = valueOf("IP_MULTICAST_TTL");public static final ChannelOption<Boolean> IP_MULTICAST_LOOP_DISABLED = valueOf("IP_MULTICAST_LOOP_DISABLED");public static final ChannelOption<Boolean> TCP_NODELAY = valueOf("TCP_NODELAY");@Deprecatedpublic static final ChannelOption<Boolean> DATAGRAM_CHANNEL_ACTIVE_ON_REGISTRATION =valueOf("DATAGRAM_CHANNEL_ACTIVE_ON_REGISTRATION");public static final ChannelOption<Boolean> SINGLE_EVENTEXECUTOR_PER_GROUP =valueOf("SINGLE_EVENTEXECUTOR_PER_GROUP");}
5.6、绑定端口启动服务
bind()方法用于绑定端口,会创建一个Channel而后启动服务。
绑定成功后,返回一个ChannelFuture,以代表是一个异步的操作。在上述的例子里,使用的是sync()方法,以同步的方式来获取服务启动的结果。
六、引导客户端
为了能更好的理解引导程序,下面就以 Echo 协议的客户端的代码为例。核心代码如下:
// 配置客户端
EventLoopGroup group = new NioEventLoopGroup();
try {Bootstrap b = new Bootstrap();b.group(group).channel(NioSocketChannel.class).option(ChannelOption.TCP_NODELAY, true).handler(new EchoClientHandler());// 连接到服务器ChannelFuture f = b.connect(hostName, portNumber).sync();Channel channel = f.channel();ByteBuffer writeBuffer = ByteBuffer.allocate(32);try (BufferedReader stdIn = new BufferedReader(new InputStreamReader(System.in))) {String userInput;while ((userInput = stdIn.readLine()) != null) {writeBuffer.put(userInput.getBytes());writeBuffer.flip();writeBuffer.rewind();// 转为ByteBufByteBuf buf = Unpooled.copiedBuffer(writeBuffer);// 写消息到管道channel.writeAndFlush(buf);// 清理缓冲区writeBuffer.clear();}} catch (UnknownHostException e) {System.err.println("不明主机,主机名为: " + hostName);System.exit(1);} catch (IOException e) {System.err.println("不能从主机中获取I/O,主机名为:" + hostName);System.exit(1);}
} finally {// 优雅的关闭group.shutdownGracefully();
}
引导 Netty 客户端主要分为几下几个步骤。
6.1、实例化引导程序类
在上述代码中,首先是需要实例化引导程序类。由于是客户端的程序,所以,实例化了一个Bootstrap。
6.2、设置 EventLoopGroup
设置Bootstrap的EventLoopGroup。不同于服务器,客户端只需要使用了一个NioEventLoopGroup。
6.3、指定 Channel 类型
channel()方法用于指定Bootstrap的Channel类型。在本例中,由于是客户端使用,使用的是NioSocketChannel类型,代表了客户端是一个基于SocketChannel的实现,使用基于 NIO 选择器的实现来发起连接请求。
6.4、设置 Channel 选项
option用于设置Channel的选项。这些选项定义在ChannelOption类中。
6.5、指定 ChannelHandler
Handler用于设置处理服务端请求的ChannelHandler。上述例子中,指定的是自定义的EchoClientHandler。
6.6、连接到服务器
connect()方法用于连接到指定的服务器的Channel。
连接成功后,返回一个ChannelFuture,以代表是一个异步的操作。在上述的例子里,使用的是sync()方法,以同步的方式来获取服务启动的结果。
总结
通过上述对引导程序类的介绍,相信大家对于服务端和客户端的引导程序以及一些配置项的都有了一定的了解。通过看源码,我们就能更加清楚的知道 Netty 服务端和客户端的启动的过程。下节我们来分析Netty的线程模型。
相关文章:

【Netty】Netty 程序引导类(九)
文章目录 前言一、引导程序类二、AbstractBootStrap 抽象类三、Bootstrap 类四、ServerBootstrap 类五、引导服务器5.1、 实例化引导程序类5.2、设置 EventLoopGroup5.3、指定 Channel 类型5.4、指定 ChannelHandler5.5、设置 Channel 选项5.6、绑定端口启动服务 六、引导客户端…...
如何使用进行MQ中间件接口测试
进行MQ中间件接口测试时,需要按以下步骤进行: 1.配置测试环境 搭建MQ服务器环境,并确保连接配置正确,以及客户端SDK等相关依赖库和组件已安装并配置正确。 2.制定测试计划 测试人员需要根据具体业务场景和测试目的,制…...

Zebec生态进展迅速,频被BitFlow、Matryx DAO等蹭热度碰瓷
进入到 2023 年以来, Zebec 生态的整体发展突飞猛进,除了流支付协议 Zebec Protocol 不断通过收购来扩大自身流支付业务、与万事达等合作推出 Zebec Card 等在支付业务上,实现进展外,其社区驱动的Layer3 模块化链 Nautilus Chain …...

7种PCB走线方式
01电源布局布线相关 数字电路很多时候需要的电流是不连续的,所以对一些高速器件就会产生浪涌电流。 如果电源走线很长,则由于浪涌电流的存在进而会导致高频噪声,而此高频噪声会引入到其他信号中去。 而在高速电路中必然会存在寄生电感和寄…...
Rabbit SpringBoot高级用法
Rabbit高级用法 一、Rabbit Springboot集成1.1 引入依赖1.2 添加配置1.3 添加Config1.4 编写Consumer1.5 发送消息 二、Rabbit 高级用法2.1 消息发送前置处理器2.2 消息发送确认机制2.3 消息接收后处理器2.4 事务消息 一、Rabbit Springboot集成 1.1 引入依赖 <dependency…...

找不到vcruntime140.dll,无法继续执行代码?多种解决方法解析
找不到vcruntime140.dll,无法继续执行代码?当你在尝试运行某个程序时,突然弹出一条错误提示框,告诉你无法继续执行代码,因为找不到vcruntime140.dll。这个问题很常见,但是它可能会让你感到困惑和疑惑。这篇文章将详细介…...

自然语言处理实战项目8- BERT模型的搭建,训练BERT实现实体抽取识别的任务
大家好,我是微学AI,今天给大家介绍一下自然语言处理实战项目8- BERT模型的搭建,训练BERT实现实体抽取识别的任务。BERT模型是一种用于自然语言处理的深度学习模型,它可以通过训练来理解单词之间的上下文关系,从而为下游…...

pdf怎么合并在一起?软件操作更高效
PDF格式已经成为了许多文档和表格的首选格式。然而,当你需要合并多个PDF文件时,可能会遇到一些麻烦,在本篇文章中,我们将向您介绍一种简单易用的方法来合并PDF文件。 以下是可以用来合并PDF文件的软件: - PDF转换器&a…...

Junit常见用法
一.Junit的含义 Junit是一种Java编程语言的单元测试框架。它提供了一些用于编写和运行测试的注释和断言方法,并且可以方便地执行测试并生成测试报告。Junit是开源的,也是广泛使用的单元测试框架之一 二.Junit项目的创建 (1)先创…...
c++—内存管理、智能指针、内存池
1. 内存分析诊断工具:valgrind; 2. 内存管理的两种方式: ①用户管理:自己申请的,自己用,自己回收;效率高,但容易导致内存泄漏; ②系统管理:系统自动回收垃圾…...
JAVA使用HTTP代码示例
以下是使用Java发送HTTP请求的示例代码: java import java.io.BufferedReader; import java.io.InputStreamReader; import java.net.HttpURLConnection; import java.net.URL; public class HttpExample { public static void main(String[] args) { try { …...

【网络协议详解】——电子邮件系统协议(学习笔记)
目录 🕒 1. 电子邮件系统概述🕒 2. 简单邮件传送协议SMTP🕒 3. SMTP协议的命令和响应🕘 3.1 命令🕤 3.1.1 HELO🕤 3.1.2 MAIL FROM🕤 3.1.3 RCPT TO🕤 3.1.4 DATA🕤 3.1.…...

年度发布 | MeterSphere一站式开源持续测试平台发布v2.10 LTS版本
2023年5月25日,MeterSphere一站式开源持续测试平台正式发布v2.10 LTS版本。这是继2022年5月发布v1.20 LTS版本后,MeterSphere开源项目发布的第三个LTS(Long Term Support)版本。MeterSphere开源项目组将对MeterSphere v2.10 LTS版…...

从 OceanBase 迁移数据到 DolphinDB
OceanBase 是一款金融级分布式关系数据库,具有数据强一致、高可用、高性能、在线扩展、高度兼容 SQL标准和主流关系数据库、低成本等特点,但是其学习成本较高,且缺乏金融计算函数以及流式增量计算的功能。 DolphinDB 是一款国产的高性能分布…...

淘宝商品列表数据接口(支持价格、销量排序)
淘宝商品列表数据接口是淘宝提供的一种可以获取淘宝商品信息的接口。通过该接口,可以获取到具有一定规则的商品信息,例如按照价格排序、按照销量排序等。接口返回的数据格式为JSON格式,可以方便地处理数据。 我们可以通过调用淘宝提供的API&…...
Android 11 版本变更总览
Android 11 版本 Android 11 总览重大隐私权变更行为变更:所有应用行为变更:以 Android 11 为目标平台的应用功能和 API 概览Intent系统广播 intent(API 级别 30)通用应用 intent(API 级别 30) Android 11 …...

传染病学模型 | Matlab实现基于SIS传染病模型模拟城市内人口的互相感染及城市人口流动所造成的传染
文章目录 效果一览基本描述模型介绍程序设计参考资料效果一览 基本描述 传染病学模型 | Matlab实现基于SIS传染病模型模拟城市内人口的互相感染及城市人口流动所造成的传染 模型介绍 SIS模型是一种基本的传染病学模型,用于描述一个人群中某种传染病的传播情况。SIS模型假设每个…...
物联网技术如何改变我们的生活:一位资深物联网专家的见解
物联网(IoT)是指通过网络互联的物理设备、车辆、建筑物以及其他物品,这些物品都内置了传感器、执行器、软件和网络连接器,使它们能够收集和交换数据。物联网技术已经在各个领域产生了深远的影响,包括家庭、医疗、交通、…...

node.js+vue.js大学生在线选课系统的设计与实现93pul
本次设计任务是要设计一个选课系统的设计与实现,通过这个系统能够满足用户对选课信息的需求。系统的主要功能包括:个人中心、学生管理、教师管理、选课信息管理等功能。 管理员可以根据系统给定的账号进行登录,登录后可以进入选课系统的设计与…...

华为OD机试真题 Java 实现【寻找符合要求的最长子串】【2023Q1 200分】
一、题目描述 给定一个字符串 s ,找出这样一个子串: 该子串中的任意一个字符最多出现2次;该子串不包含指定某个字符;请你找出满足该条件的最长子串的长度。 二、输入描述 第一行为要求不包含的指定字符,为单个字符,取值范围[0-9a-zA-Z]。 第二行为字符串s,每个字符范…...
Linux 中替换文件中的某个字符串
如果你想在 Linux 中替换文件中的某个字符串,可以使用以下命令: 1. 基本替换(sed 命令) sed -i s/原字符串/新字符串/g 文件名示例:将 file.txt 中所有的 old_text 替换成 new_text sed -i s/old_text/new_text/g fi…...
n8n:解锁自动化工作流的无限可能
在当今快节奏的数字时代,无论是企业还是个人,都渴望提高工作效率,减少重复性任务的繁琐操作。而 n8n,这个强大的开源自动化工具,就像一位智能的数字助手,悄然走进了许多人的工作和生活,成为提升…...

【电路笔记】-变压器电压调节
变压器电压调节 文章目录 变压器电压调节1、概述2、变压器电压调节3、变压器电压调节示例14、变压器电压调节示例25、变压器电压调节示例36、总结变压器电压调节是变压器输出端电压因连接负载电流的变化而从其空载值向上或向下变化的比率或百分比值。 1、概述 电压调节是衡量变…...

基于FPGA的超声波显示水位距离,通过蓝牙传输水位数据到手机,同时支持RAM存储水位数据,读取数据。
基于FPGA的超声波显示水位距离 前言一、整体框架二、代码架构1.超声波测距模块2.蓝牙数据发送模块3.数码管数据切换模块4.数码管驱动模块6.串口驱动7.顶层模块8.RAM ip核 仿真相关截图 前言 随着工业化进程的加速和环境保护意识的提升,对水资源管理和水位监测的需求…...

vmware 设置 dns
vmware 设置 dns 常用的 DNS(Domain Name System)服务器地址可以帮助你更快、更安全地解析域名。以下是一些国内外常用的公共 DNS 服务: 国内常用 DNS 阿里云 DNS IPv4: 223.5.5.5、223.6.6.6IPv6: 2400:3200::1、2400:3200:baba::1特点&am…...

uni-app学习笔记二十四--showLoading和showModal的用法
showLoading(OBJECT) 显示 loading 提示框, 需主动调用 uni.hideLoading 才能关闭提示框。 OBJECT参数说明 参数类型必填说明平台差异说明titleString是提示的文字内容,显示在loading的下方maskBoolean否是否显示透明蒙层,防止触摸穿透,默…...

分类场景数据集大全「包含数据标注+训练脚本」 (持续原地更新)
一、作者介绍:六年算法开发经验、AI 算法经理、阿里云专家博主。擅长:检测、分割、理解、大模型 等算法训练与推理部署任务。 二、数据集介绍: 质量高:高质量图片、高质量标注数据,吐血标注、整理,可以作为…...
java复习 05
我的天啊一天又要过去了,没事的还有时间!!! 不要焦虑不要焦虑,事实证明只要我认真地投入进去一切都还是来得及的,代码多实操多复盘,别叽叽喳喳胡思乱想多多思考,有迷茫前害怕后的功…...

Linux--命令行参数和环境变量
1.命令行参数 Linux 命令行参数基础 1.1参数格式 位置参数:无符号,按顺序传递(如 ls /home/user 中 /home/user 是位置参数) 选项参数: 短选项:以 - 开头,单个字母(如 -l 表示长格…...
csharp基础....
int[][] jaggedArray new int[3][]; jaggedArray[0] new int[] { 1, 2 }; jaggedArray[1] new int[] { 3, 4, 5 }; jaggedArray[2] new int[] { 6, 7, 8, 9 }; 嵌套 反转和排序 List<int> list new List<int> { 1, 2, 3, 4, 5 }; list.Reverse(); Cons…...