io.netty学习(十)Netty 程序引导类

news2024/11/13 19:02:58

目录

前言

引导程序类

AbstractBootStrap 抽象类

Bootstrap 类

ServerBootstrap 类

引导服务器

1、实例化引导程序类

2、设置 EventLoopGroup

3、指定 Channel 类型

4、指定 ChannelHandler

5、设置 Channel 选项

6、绑定端口启动服务

引导客户端

1、实例化引导程序类

2、设置 EventLoopGroup

3、指定 Channel 类型

4、设置 Channel 选项

5、指定 ChannelHandler

6、连接到服务器

总结


前言

程序引导类(Bootstrap)可以理解为是一个程序的入口程序,在 Java 程序中,就是一个包含 main 方法的程序类。在 Netty 中,引导程序还包含一系列的配置项。本篇文章我们就来介绍 Netty 的引导程序。

io.netty学习使用汇总 

引导程序类

引导程序类是一种引导程序,使 Netty 程序可以很容易地引导一个 Channel。在 Netty 中,承担引导程序的是 AbstractBootStrap抽象类。

引导程序类都在io.netty.bootstrap包下。AbstractBootStrap抽象类有两个子类:BootstrapServerBootstrap,分别用于引导客户端程序及服务的程序。

下图展示了引导程序类的关系:

AbstractBootStrap 抽象类

从上图可以看出,AbstractBootStrap抽象类实现了Cloneable接口。那么为什么需要实现Cloneable接口呢?

在 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所要管理的启动配置包括EventLoopGroupSocketAddressChannel配置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,其将被应用到每个新创建的ChannelChannelConfig。这些选项将会通过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 */
    @Deprecated
    public 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:指定要应用到新创建的ServerChannelChannelConfigChannelOption。这些选项将会通过bind()设置到Channel,在bind()方法被调用之后,设置或者改变ChannelOption将不会有任何效果。支持什么样的ChannelOption取决于所使用的Channel类型。

  • childOption:指定当子Channel被接受时,应用到子ChannelChannelConfigChannelOption。所支持的ChannelOption取决于所使用的Channel类型。

  • attr:指定ServerChannel上的属性值。这些属性值是通过bind()或者connect()方法设置到Channel。在bind()方法被调用之后它们将不会有任何效果。

  • childAttr:将属性设置给已经被接受的子Channel。之后再次调用将不会有任何效果。

  • handler:设置被添加到ServerChannelChannelPipeline中的ChannelHandler

  • childHandler:设置将被添加到已被接受的子ChannelChannelPipeline中的ChannelHandler

  • clone:克隆一个设置好原始的ServerBootstrap相同的ServerBootstrap

  • bind:绑定ServerChannel并返回一个ChannelFuture,其将会在绑定操作完成之后接收到通知(带着成功或者失败的结果)。

ServerBootstrap类中许多方法都继承自AbstractBootstrap类。

引导服务器

为了能更好的理解引导程序,下面就以 Echo 协议的服务器的代码为例。核心代码如下:

// 多线程事件循环器
EventLoopGroup bossGroup = new NioEventLoopGroup(); // boss
EventLoopGroup workerGroup = new NioEventLoopGroup(); // worker

try {
    // 启动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 服务器主要分为几下几个步骤。

1、实例化引导程序类

在上述代码中,首先是需要实例化引导程序类。由于是服务器端的程序,所以,实例化了一个ServerBootstrap

2、设置 EventLoopGroup

设置ServerBootstrapEventLoopGroup。上述服务器使用了两个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 = 0
        this(0);
    }

    /**
     * @see MultithreadEventExecutorGroup#MultithreadEventExecutorGroup(int, Executor, Object...)
     */
    protected MultithreadEventLoopGroup(int nThreads, Executor executor, Object... args) {
        // 如果不指定(nThreads = 0),默认值是 DEFAULT_EVENT_LOOP_THREADS
        super(nThreads == 0 ? DEFAULT_EVENT_LOOP_THREADS : nThreads, executor, args);
    }

从上述源码可以看出,如果NioEventLoopGroup实例化时,没有指定线程数,则最终默认值是DEFAULT_EVENT_LOOP_THREADS,而默认值DEFAULT_EVENT_LOOP_THREADS是根据当前机子的CPU 处理的个数乘以 2 得出的。

3、指定 Channel 类型

channel()方法用于指定ServerBootstrapChannel类型。在本例中,使用的是NioServerSocketChannel类型,代表了服务器是一个基于ServerSocketChannel的实现,使用基于 NIO 选择器的实现来接受新连接。

4、指定 ChannelHandler

childHandler用于指定ChannelHandler,以便处理Channel的请求。上述例子中,指定的是自定义的EchoServerHandler

5、设置 Channel 选项

optionchildOption方法,分别用于设置ServerChannelServerChannel的子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)}.
     */
    @Deprecated
    public 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}
     */
    @Deprecated
    public static final ChannelOption<Integer> WRITE_BUFFER_HIGH_WATER_MARK = valueOf("WRITE_BUFFER_HIGH_WATER_MARK");
    /**
     * @deprecated Use {@link #WRITE_BUFFER_WATER_MARK}
     */
    @Deprecated
    public 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");

    @Deprecated
    public 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");

}

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();

            // 转为ByteBuf
            ByteBuf 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 客户端主要分为几下几个步骤。

1、实例化引导程序类

在上述代码中,首先是需要实例化引导程序类。由于是客户端的程序,所以,实例化了一个Bootstrap

2、设置 EventLoopGroup

设置BootstrapEventLoopGroup。不同于服务器,客户端只需要使用了一个NioEventLoopGroup

3、指定 Channel 类型

channel()方法用于指定BootstrapChannel类型。在本例中,由于是客户端使用,使用的是NioSocketChannel类型,代表了客户端是一个基于SocketChannel的实现,使用基于 NIO 选择器的实现来发起连接请求。

4、设置 Channel 选项

option用于设置Channel的选项。这些选项定义在ChannelOption类中。

5、指定 ChannelHandler

Handler用于设置处理服务端请求的ChannelHandler。上述例子中,指定的是自定义的EchoClientHandler

6、连接到服务器

connect()方法用于连接到指定的服务器的Channel

连接成功后,返回一个ChannelFuture,以代表是一个异步的操作。在上述的例子里,使用的是sync()方法,以同步的方式来获取服务启动的结果。

总结

通过上述对引导程序类的介绍,相信大家对于服务端和客户端的引导程序以及一些配置项的都有了一定的了解。通过看源码,我们就能更加清楚的知道 Netty 服务端和客户端的启动的过程。下节我们来分析Netty的线程模型。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

设计模式之代理模式笔记

设计模式之代理模式笔记 说明Proxy(代理)目录代理模式静态代理示例类图买火车票的接口火车站类代售点类测试类 JDK动态代理买火车票的接口火车站类获取代理对象的工厂类测试类 CGLIB动态代理火车站类代理工厂类测试类 三种代理对比优缺点 说明 记录下学习设计模式-代理模式的写…

windows pwn

环境搭建 checksec winchecksec winchecksec 是 windows 版的 checksec &#xff0c;不过有时候结果不太准确。 checksec&#xff08;x64dbg&#xff09; x64dbg 的插件 checksec 检查效果比较准确&#xff0c;并且可以连同加载的 dll 一起检测。 将 release 的插件按 3…

RK3288 Android8.1添加EC25

首先拿到供应商提供的so库&#xff0c;将so放到vendor\rockchip\common\phone\lib下 修改对应的phone.mk&#xff0c;将so库移动指定位置&#xff08;Android7以下移动到system/lib,android8以后移动到vendor/lib&#xff09; CUR_PATH : vendor/rockchip/common#############…

mysql避免重复插入记录insert ignore 、on duplicate key update、replace into

星标▲Java学习之道▲一起成长&#xff0c;一起学习~ 哈喽&#xff0c;大家好&#xff0c;我是阿淼。今天梳理一下mysql中避免重复插入记录的集中操作。 1序 回顾以前写的项目&#xff0c;发现在规范的前提下&#xff0c;还是可以做点骚操作的。 假如项目使用的MySQL&#xff0…

基于Informer的股票价格预测(量化交易综述)

摘要 股票市场是金融市场中不可或缺的组成部分。准确预测股票趋势对于投资者和市场参与者具有重要意义&#xff0c;因为它们可以指导投资决策、优化投资组合以及降低金融风险。而且可以提升国家国际地位以及金融风险控制能力&#xff0c;还可以促进股票市场发展以及资源优化利…

Java常用类库与技巧

1、String&#xff0c;StringBuffer&#xff0c;StringBuilder的区别&#xff1f; 2、Java异常 异常处理机制主要回答了三个问题 What&#xff1a;异常类型回答了什么被抛出&#xff1f;Where&#xff1a;异常堆栈跟踪回答了在哪抛出&#xff1f;Why&#xff1a;异常信息回答…

PowerDesigner165安装

PowerDesigner安装及解析 一、PowerDesigner安装1.双击开始安装2.一路“Next”3.选择地区4.安装路径5.按图勾选6.一路“Next”7.安装中8.安装完成 二、解析三、使用 一、PowerDesigner安装 1.双击开始安装 2.一路“Next” 3.选择地区 选择软件安装所属地区,一定要选择“Hong …

vue3-实战-12-管理后台-权限管理之菜单管理模块-首页-主题颜色-暗黑模式

目录 1-列表页面功能开发 1.1-需求原型分析 1.2-接口和数据类型定义 1.3-获取服务端数据渲染页面 2-新增编辑菜单 2.1-原型需求分析 2.2-表单数据收集和页面结构开发 2.3-提交或者取消 3-删除菜单 4-首页开发 5-暗黑模式的切换和主题颜色 5.1-暗黑模式 5.2-主题颜…

three.js几何体的_UV_、法向属性以及BufferGeometry类介绍

一、几何体的_UV_以及法向属性 UV属性是一组二维坐标&#xff0c;每个顶点都有一个对应的UV坐标。在三维模型上贴上二维的纹理贴图时&#xff0c;需要将所有顶点映射到纹理上的对应位置。UV属性的取值范围一般是[0,1]&#xff0c;表示纹理上的相对位置。通过修改UV属性&#xf…

Shell - 02_shell变量

一、shell的自定义变量 1.定义变量&#xff1a;变量名变量值 如&#xff1a;num10 2.引用变量&#xff1a;$变量名 如&#xff1a;i$num 把变量 num 的值付给变量 i 3.显示变量&#xff1a;使用 echo 命令可以显示单个变量取值 如&#xff1a;echo $num 4.清除变量&…

如何写好接口自动化测试脚本

谈到接口测试&#xff0c;大家关注更多的是哪个工具更优秀&#xff0c;更好用。但是很少人关注到接口测试用例的设计问题&#xff0c;也很少人会去写接口用例&#xff0c;都代码化了嘛&#xff0c;还写什么用例&#xff0c;是吧&#xff1f; 这样真的对么&#xff1f;我们是不…

Web3通过 MetaMask简单演示对ganache虚拟环境账号进行管理操作

上文 Web3通过ganache运行起一个本地虚拟区块链 我们通过ganache在本地运行起了一个虚拟的区块链环境 那么 接下来 我们就要用 MetaMask 来管理这个东西了 如果您还没有安装 可以访问文章Web3 将 MetaMask添加入谷歌浏览器 扩展程序中和Web3开发准备工作 手把手带你创建自己的 …

行业报告 | 人工智能时代的营销新趋势

原创 | 文 BFT机器人 01 科技推动时代发展进步 随着电子计算机的发明和使用&#xff0c;打开了人类知识的全方位信息时空&#xff0c;人类由此从工业文明走进信息文明&#xff0c;渐渐地网络成为了人们进行社会活动的基本平台。 智能手机的出现将人们剩余的碎片化时间也连接到了…

从尾到头打印链表

输入一个链表的头节点&#xff0c;按链表从尾到头的顺序返回每个节点的值&#xff08;用数组返回&#xff09;。 如输入{1,2,3}的链表如下图: ​ 返回一个数组为[3,2,1] 0 < 链表长度 < 10000 示例1 输入&#xff1a; {1,2,3} 返回值&#xff1a; [3,2,1]示例2 输入…

springboot集成J-IM+vue实现简单的聊天功能

前言&#xff1a;看了demo自己摸索着集成了一下&#xff0c;特此记录 一、引入依赖 <!-- jim-server --> <dependency><groupId>org.j-im</groupId><artifactId>jim-server</artifactId><version>3.0.0.v20200501-RELEASE&l…

【系统开发】尚硅谷 - 谷粒商城项目笔记(六):异步线程池

文章目录 异步线程池讲解简单线程池常见的四种线程池进阶线程池为什么使用线程池异步编排基本用法其他API线程串行化两任务组合都完成时一个完成时 多任务组合 异步线程池讲解 简单线程池 public class Test01 {public static void main(String[] args) {// 声明一个有10个线…

Java——集合

文章目录 1、集合概述2、集合类体系结构Collection集合体系 3、Collection集合常用API3、Collection集合的遍历方式方式一&#xff1a;迭代器方式二&#xff1a;foreach/增强for循环方式三&#xff1a;lambda表达式 4、List系列集合List集合特点和特有APILinkedList集合 5、集合…

物流园仓库智能综合监控系统

现代经济的不断发展&#xff0c;仓储物流业也在快速地发展&#xff0c;物流仓库作为物质资源的存储和转运&#xff0c;在经济生产中发挥着重大的作用&#xff0c;但是在此期间&#xff0c;随之而来的是物品丢失、被盗、损坏等一系列安全隐患事件。 物流仓库里面存储物品的多数都…

nginx: client intended to send too large body

最近上传大于1M文件的时候&#xff0c;报错nginx。 413 Request Entity Too Large 经过排查修改nginx配置 这是最简单的一个做法&#xff0c;着报错原因是nginx不允许上传配置过大的文件&#xff0c;那么件把nginx的上传大小配置调高就好。 1、打开nginx主配置文件nginx.co…

【性能测试】loadrunner12.55教程(一)--知识准备

目录 1.0. 前言 1.1 性能测试术语介绍 1.1.1 响应时间&#xff08;Response time&#xff09; 1.1.2 并发用户数 1.1.3 吞吐量&#xff08;Throughput&#xff09; 1.1.4 吞吐率&#xff08;Throughout&#xff09; 1.1.5 TPS&#xff08;Transaction Per Second&#x…