Netty实战(四)

news2024/11/25 18:27:24

本节我们看看Netty的传输(全是干货,自带水杯)

  • 一、Java的NIO和OIO
    • 1.1 OIO
    • 1.2 NIO
  • 二、Netty的NIO和OIO
    • 2.1 OIO
    • 2.2 NIO
  • 三、传输API
  • 四、内置的传输
    • 4.1 NIO
    • 4.2 Epoll—用于 Linux 的本地非阻塞传输
    • 4.3 OIO
    • 4.4 用于 JVM 内部通信的 Local 传输
    • 4.5 Embedded 传输
  • 五、传输的用例

一、Java的NIO和OIO

流经网络的数据总是具有相同的类型:字节。这些字节是如何流动的主要取决于我们所说的网络传输。

1.1 OIO

我们先来看一段Java的阻塞应用程序程序:

package com.example.javademo;

import java.io.IOException;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.charset.Charset;

/**
 * @author lhd
 * @date 2023/05/17 13:19
 * @notes java oio演示
 */
public class PlainOioServer {

    public void serve(int port) throws IOException {
        //绑定服务器到指定端口
        final ServerSocket socket = new ServerSocket(port);
        try {
            for (;;) {
                //接受连接
                final Socket clientSocket = socket.accept();
                System.out.println(
                        "Accepted connection from " + clientSocket);
                //创建一个新的线程来处理该连接
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        OutputStream out;
                        try {
                            out = clientSocket.getOutputStream();
                            //将消息写给新连接的客户端
                            out.write("Hi!\r\n".getBytes(Charset.forName("UTF-8")));
                            out.flush();
                            //关闭连接
                            clientSocket.close();
                        }
                        catch (IOException e) {
                            e.printStackTrace();
                        }
                        finally {
                            try {
                                clientSocket.close();
                            }
                            catch (IOException ex) {
                            // 关闭时忽略
                            }
                        }
                    }
                }).start(); //启动线程
            }
        }
        catch (IOException e) {
            e.printStackTrace();
        }
    }
}

这段代码完全可以处理中等数量的并发客户端。但是随着应用程序变得流行起来,你会发现它并不能很好地伸缩到支撑成千上万的并发连入连接。

1.2 NIO

同样,来看一段Java的非阻塞应用程序代码:

package com.example.javademo;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;

/**
 * @author lhd
 * @date 2023/05/17 13:27
 * @notes java NIO演示
 */
public class PlainNioServer {

    public void serve(int port) throws IOException {
        ServerSocketChannel serverChannel = ServerSocketChannel.open();
        serverChannel.configureBlocking(false);
        ServerSocket ssocket = serverChannel.socket();
        //将服务绑定到端口
        InetSocketAddress address = new InetSocketAddress(port);
        ssocket.bind(address);
        //打开选择器来处理Channel
        Selector selector = Selector.open();
        //将serverChannel注册到选择器用来接收连接
        serverChannel.register(selector, SelectionKey.OP_ACCEPT);
        final ByteBuffer msg = ByteBuffer.wrap("Hi!\r\n".getBytes());
        for (;;) {
            try {
                //等待需要处理的新事件;阻塞 将一直持续到下一个传入事件
                selector.select();
            } catch (IOException ex) {
                ex.printStackTrace();
           // handle exception
                break;
            }
            //获取所有接收事件的SelectionKey 实例
            Set<SelectionKey> readyKeys = selector.selectedKeys();
            Iterator<SelectionKey> iterator = readyKeys.iterator();
            while (iterator.hasNext()) {
                SelectionKey key = iterator.next();
                iterator.remove();
                try {
                    //检查事件是否是一个新的已经就绪可以被接受的连接
                    if (key.isAcceptable()) {
                        ServerSocketChannel server = (ServerSocketChannel)key.channel();
                        SocketChannel client = server.accept();
                        client.configureBlocking(false);
                        //接受客户端,并将它注册到选择器
                        client.register(selector, SelectionKey.OP_WRITE |
                                SelectionKey.OP_READ, msg.duplicate());
                        System.out.println("Accepted connection from " + client);
                    }
                    //检查套接字是否已经准备好写数据
                    if (key.isWritable()) {
                        SocketChannel client = (SocketChannel)key.channel();
                        ByteBuffer buffer = (ByteBuffer)key.attachment();
                        while (buffer.hasRemaining()) {
                            //将数据写到已连接的客户端
                            if (client.write(buffer) == 0) {
                                break;
                            }
                        }
                        //关闭连接
                        client.close();
                    }
                } catch (IOException ex) {
                    key.cancel();
                    try {
                        key.channel().close();
                    } catch (IOException cex) {
                    }
                }
            }
        }
    }
}

可以看出,两段Java的OIO和NIO都做了相同的事:连接客户端,并发送“Hi”。区别在于一个是阻塞的,另一个是非阻塞的,但两段代码却完全不同。如果为了用于非阻塞 I/O 而重新实现这个简单的应用程序,都需要一次完全的重写的话,那么不难想象,移植真正复杂的应用程序需要付出什么样的努力。

二、Netty的NIO和OIO

下来我们来看以下Netty 实现该应用程序将会是什么样子。

2.1 OIO

来看一段Netty的阻塞代码

package com.example.netty;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.*;
import io.netty.channel.oio.OioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.oio.OioServerSocketChannel;

import java.net.InetSocketAddress;
import java.nio.charset.StandardCharsets;

/**
 * @author lhd
 * @date 2023/05/17 13:47
 * @notes Netty 阻塞程序演示
 * ps:Netty已启用OIO传输,使用 NIO / EPOLL / KQUEUE 传输。
 */
public class NettyOioServer {
    public void server(int port) throws InterruptedException {
        final ByteBuf buf = Unpooled.unreleasableBuffer(Unpooled.copiedBuffer("Hi!\r\n", StandardCharsets.UTF_8));
        EventLoopGroup group = new OioEventLoopGroup();
        try {
            //创建ServerBootstrap
            ServerBootstrap b = new ServerBootstrap();
            b.group(group)
                    //使用 OioEventLoopGroup以允许阻塞模式
                    .channel(OioServerSocketChannel.class)
                    .localAddress(new InetSocketAddress(port))
                    //指定 ChannelInitializer,对于每个已接受的连接都调用它
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        public void initChannel(SocketChannel ch){
                            //添加一个 ChannelInboundHandlerAdapter 以拦截和处理事件
                            ch.pipeline().addLast(new ChannelInboundHandlerAdapter() {
                                @Override
                                public void channelActive(ChannelHandlerContext ctx) {
                                    //将消息写到客户端,并添加 ChannelFutureListener,以便消息一被写完就关闭连接
                                    ctx.writeAndFlush(buf.duplicate()).addListener(ChannelFutureListener.CLOSE);
                                }
                            });
                        }
                    });
            //绑定服务器以接受连接
            ChannelFuture f = b.bind().sync();
            //释放所有的资源
            f.channel().closeFuture().sync();
        } finally {
            group.shutdownGracefully().sync();
        }
    }
}

ps:这段代码和我们上一篇文章中的有所区别,不要搞混淆了

2.2 NIO

我们再来看看Netty的非阻塞代码:

package com.example.netty;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;

import java.net.InetSocketAddress;
import java.nio.charset.StandardCharsets;

/**
 * @author lhd
 * @date 2023/05/17 14:05
 * @notes Netty 非阻塞代码演示
 */
public class NettyNioServer {

    public void server(int port) throws InterruptedException {
        final ByteBuf buf = Unpooled.unreleasableBuffer(Unpooled.copiedBuffer("Hi!\r\n", StandardCharsets.UTF_8));
       EventLoopGroup group = new NioEventLoopGroup(); // 1
        try {
            ServerBootstrap b = new ServerBootstrap();
            //为非阻塞模式使用NioEventLoopGroup
            b.group(group).channel(NioServerSocketChannel.class) // 2
                    .localAddress(new InetSocketAddress(port))
                    //指定 ChannelInitializer,对于每个已接受的连接都调用它
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        public void initChannel(SocketChannel ch) {
                            //添加 ChannelInboundHandlerAdapter 以接收和处理事件
                            ch.pipeline().addLast(new ChannelInboundHandlerAdapter() {
                                @Override
                                public void channelActive(ChannelHandlerContext ctx) {
                                    //将消息写到客户端,并添加ChannelFutureListener,以便消息一被写完就关闭连接
                                    ctx.writeAndFlush(buf.duplicate()).addListener(ChannelFutureListener.CLOSE);
                                }
                            });
                        }
                    });
            //绑定服务器以接受连接
            ChannelFuture f = b.bind().sync();
            f.channel().closeFuture().sync();
        } finally {
            //释放所有的资源
            group.shutdownGracefully().sync();
        }
    }
}

可以看到,Netty的阻塞代码和非阻塞代码只有两处不同(代码中1 2 处)。这使得Netty的阻塞和非阻塞切换非常容易,Netty 为每种传输的实现都暴露了相同的 API,所以无论选用哪一种传输的实现,你的代码都仍然几乎不受影响。在所有的情况下,传输的实现都依赖于 interface Channel、ChannelPipeline 和 ChannelHandler。

三、传输API

传输 API 的核心是 interface Channel,它被用于所有的 I/O 操作。下图是它的层次结构:
在这里插入图片描述
如图所示,每个 Channel 都将会被分配一个 ChannelPipeline和ChannelConfig。ChannelConfig 包含了该 Channel 的所有配置设置,并且支持热更新。由于特定的传输可能具有独特的设置,所以它可能会实现一个 ChannelConfig 的子类型。

由于 Channel 是独一无二的,所以为了保证顺序将 Channel 声明为 java.lang.Comparable 的一个子接口。因此,如果两个不同的 Channel 实例都返回了相同的散列码,那么 AbstractChannel 中的 compareTo()方法的实现将会抛出一个 Error。

ChannelPipeline 持有所有将应用于入站和出站数据以及事件的 ChannelHandler 实例,这些 ChannelHandler 实现了应用程序用于处理状态变化以及数据处理的逻辑。

ChannelHandler 的典型用途包括:

  • 将数据从一种格式转换为另一种格式;
  • 提供异常的通知;
  • 提供 Channel 变为活动的或者非活动的通知;
  • 提供当 Channel 注册到 EventLoop 或者从 EventLoop 注销时的通知;
  • 提供有关用户自定义事件的通知。

ps:ChannelPipeline 实现了一种常见的设计模式—拦截过滤器(InterceptingFilter)。UNIX 管道是另外一个熟悉的例子:多个命令被链接在一起,其中一个命令的输出端将连接到命令行中下一个命令的输入端。

也可以根据需要通过添加或者移除ChannelHandler实例来修改ChannelPipeline。
通过利用Netty的这项能力可以构建出高度灵活的应用程序。例如,STARTTLS方法名协议被请求时,你可以简单地通过 向 ChannelPipeline 添 加 一个适当的 ChannelHandler(SslHandler)来按需地支持STARTTLS协议。除了访问所分配的 ChannelPipeline 和 ChannelConfig 之外,也可以利用 Channel的其他方法。

比较重要的Channel方法如下:

方法名描述
eventLoop返回分配给 Channel 的 EventLoop
pipeline返回分配给 Channel 的 ChannelPipeline
isActive如果 Channel 是活动的,则返回 true。活动的意义可能依赖于底层的传输。例如,一个 Socket 传输一旦连接到了远程节点便是活动的,而一个 Datagram 传输一旦被打开便是活动的
localAddress返回本地的 SokcetAddress
remoteAddress返回远程的 SocketAddress
write将数据写到远程节点。这个数据将被传递给 ChannelPipeline,并且排队直到它被冲刷
flush将之前已写的数据冲刷到底层传输,如一个 SocketwriteAndFlush 一个简便的方法,等同于调用 write()并接着调用 flush()

Netty 所提供的广泛功能只依赖于少量的接口,所以我们可以对我们的应用程序逻辑进行重大的修改,而又无需大规模地重构你的代码库。

下面是一个写数据并将其冲刷到远程节点的例子。

Channel channel = ...
//创建要发送的数据
ByteBuf buf = Unpooled.copiedBuffer("your data", CharsetUtil.UTF_8);
//写数据并冲刷它
ChannelFuture cf = channel.writeAndFlush(buf);
//添加监听,以便完后接收通知
cf.addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture future) {
//如果成功,在控制台打印成功
if (future.isSuccess()) {
System.out.println("Write successful");
} else {
//错误则打印错误和堆栈跟踪
System.err.println("Write error");
future.cause().printStackTrace();
}
}
});

ps:Netty 的 Channel 实现是线程安全的,因此你可以存储一个到 Channel 的引用,并且每当你需要向远程节点写数据时,都可以使用它,即使当时许多线程都在使用它。

上一篇博文说过,一个Channel对应一个eventLoop,一个eventLoop持有一个线程却不一定只持有一个Channel。下面展示多个线程使用同一个Channel。

final Channel channel = ...
//创建数据
final ByteBuf buf = Unpooled.copiedBuffer("your data",
CharsetUtil.UTF_8).retain();
//创建写数据的Runable
Runnable writer = new Runnable() {
@Override
public void run() {
channel.writeAndFlush(buf.duplicate());
}
};
//获取线程池的引用
Executor executor = Executors.newCachedThreadPool();
//递交任务给线程池,以便某个线程调用
executor.execute(writer);
//递交另一个写任务以便在另一个线程中执行
executor.execute(writer);
...

这些消息将会保证按照顺序发送~

四、内置的传输

Netty 内置了一些可开箱即用的传输。因为并不是它们所有的传输都支持每一种协议,所以你必须选择一个和你的应用程序所使用的协议相容的传输。

下面是一些Netty提供的传输。

名 称描 述
NIOio.netty.channel.socket.nio使用 java.nio.channels 包作为基础——基于选择器的方式
Epollio.netty.channel.epoll由 JNI 驱动的 epoll()和非阻塞 IO。这个传输支持只有在Linux上可用的多种特性,如SO_REUSEPORT,比 NIO 传输更快,而且是完全非阻塞的
OIOio.netty.channel.socket.oio使用 java.net 包作为基础使用阻塞流
Localio.netty.channel.local可以在 VM 内部通过管道进行通信的本地传输
Embeddedio.netty.channel.embeddedEmbedded 传输,允许使用 ChannelHandler 而又不需要一个真正的基于网络的传输。这在测试你的ChannelHandler 实现时非常有用

之后我们回详细介绍这些传输。

ps:Epoll这个是 Netty 特有的实现,更加适配 Netty 现有的线程模型,具有更高的性能以及更低的垃圾回收压力

4.1 NIO

NIO 提供了一个所有 I/O 操作的全异步的实现。它利用了自 NIO 子系统被引入JDK 1.4 时便可用的基于选择器的 API。

选择器背后的基本概念是充当一个注册表,在那里你将可以请求在 Channel 的状态发生变化时得到通知。可能的状态变化有:

  • 新的 Channel 已被接受并且就绪;
  • Channel 连接已经完成;
  • Channel 有已经就绪的可供读取的数据;
  • Channel 可用于写数据。

选择器运行在一个检查状态变化并对其做出相应响应的线程上,在应用程序对状态的改变做出响应之后,选择器将会被重置,并将重复这个过程。

下表中的常量值代表了由class java.nio.channels.SelectionKey定义的位模式。
这些位模式可以组合起来定义一组应用程序正在请求通知的状态变化集。

名称描述
OP_ACCEPT请求在接受新连接并创建 Channel 时获得通知
OP_CONNECT请求在建立一个连接时获得通知
OP_READ请求当数据已经就绪,可以从 Channel 中读取时获得通知
OP_WRITE请求当可以向 Channel 中写更多的数据时获得通知。这处理了套接字缓冲区被完全填满时的情况,这种情况通常发生在数据的发送速度比远程节点可处理的速度更快的时候

内置传输的处理流程图:
在这里插入图片描述

ps:零拷贝(zero-copy)是一种目前只有在使用 NIO 和 Epoll 传输时才可使用的特性。它使你可以快速高效地将数据从文件系统移动到网络接口,而不需要将其从内核空间复制到用户空间,其在像 FTP 或者HTTP 这样的协议中可以显著地提升性能。但是,并不是所有的操作系统都支持这一特性。特别地,它对于实现了数据加密或者压缩的文件系统是不可用的——只能传输文件的原始内容。反过来说,传输已被加密的文件则不是问题

4.2 Epoll—用于 Linux 的本地非阻塞传输

Netty 的 NIO 传输基于 Java 提供的异步/非阻塞网络编程的通用抽象。虽然这保证了 Netty 的非阻塞 API 可以在任何平台上使用,但它也包含了相应的限制,因为 JDK为了在所有系统上提供相同的功能,必须做出妥协。

Netty为Linux提供了一组NIO API,其以一种和它本身的设计更加一致的方式使用epoll,并且以一种更加轻量的方式使用中断。如果你的应用程序旨在运行于Linux系统,那么请考虑利用这个版本的传输;你将发现在高负载下它的性能要优于JDK的NIO实现。

这个传输的语义和上一节内置传输的处理流程图 所示的完全相同,而且它的用法也是简单直接的。相关示例参照上面的Netty NIO代码。如果要在那个代码中使用 epoll 替代 NIO,只需要将 NioEventLoopGroup替换为EpollEventLoopGroup ,并且将 NioServerSocketChannel.class 替换为EpollServerSocketChannel.class 即可。

4.3 OIO

这个可以参照Netty的OIO代码即可,最新的Netty中抛弃了OIO的传输方式~

下图是OIO的传输处理逻辑:
在这里插入图片描述

4.4 用于 JVM 内部通信的 Local 传输

Netty 提供了一个 Local 传输,用于在同一个 JVM 中运行的客户端和服务器程序之间的异步通信。同样,这个传输也支持对于所有 Netty 传输实现都共同的 API。

在这个传输中,和服务器 Channel 相关联的 SocketAddress 并没有绑定物理网络地址;相反,只要服务器还在运行,它就会被存储在注册表里,并在 Channel 关闭时注销。因为这个传输并不接受真正的网络流量,所以它并不能够和其他传输实现进行互操作。因此,客户端希望连接到(在同一个 JVM 中)使用了这个传输的服务器端时也必须使用它。除了这个限制,它的使用方式和其他的传输一模一样。

4.5 Embedded 传输

Netty 提供了一种额外的传输,使得你可以将一组 ChannelHandler 作为帮助器类嵌入到其他的 ChannelHandler 内部。通过这种方式,你将可以扩展一个ChannelHandler 的功能,而又不需要修改其内部代码。

Embedded 传输的关键是一个被称为 EmbeddedChannel 的具体的 Channel
实现。我将在之后的文章中为大家演示这种方式。

五、传输的用例

Netty支持的传输和网络协议:

传 输TCPUDPSCTPUDT
NIO××××
Epoll(仅 Linux)××--
OIO××××

PS:UDT 协议实现了基于 UDP 协议的可靠传输;

在 Linux 上启用 SCTP
SCTP 需要内核的支持,并且需要安装用户库。
例如,对于 Ubuntu,可以使用下面的命令:# sudo apt-get install libsctp1
对于 Fedora,可以使用 yum:#sudo yum install kernel-modules-extra.x86_64 lksctp-tools.x86_64

虽然只有SCTP传输有这些特殊要求,但是其他传输可能也有它们自己的配置选项需要考虑。此外,如果只是为了支持更高的并发连接数,服务器平台可能需要配置得和客户端不一样。

这里是一些很可能会遇到的用例

  • 非阻塞代码库——如果你的代码库中没有阻塞调用(或者你能够限制它们的范围),那么在 Linux 上使用 NIO 或者 epoll 始终是个好主意。虽然 NIO/epoll 旨在处理大量的并发连接,但是在处理较小数目的并发连接时,它也能很好地工作,尤其是考虑到它在连接之间共享线程的方式。
  • 阻塞代码库——正如我们已经指出的,如果你的代码库严重地依赖于阻塞 I/O,而且你的应用程序也有一个相应的设计,那么在你尝试将其直接转换为 Netty 的 NIO 传输时,你将可能会遇到和阻塞操作相关的问题。不要为此而重写你的代码,可以考虑分阶段迁移:先从OIO 开始,等你的代码修改好之后,再迁移到 NIO(或者使用 epoll,如果你在使用 Linux)。
  • 在同一个 JVM 内部的通信——在同一个 JVM 内部的通信,不需要通过网络暴露服务,是Local 传输的完美用例。这将消除所有真实网络操作的开销,同时仍然使用你的 Netty 代码库。如果随后需要通过网络暴露服务,那么你将只需要把传输改为 NIO 或者 OIO 即可。
  • 测试你的 ChannelHandler 实现——如果你想要为自己的 ChannelHandler 实现编写单元测试,那么请考虑使用 Embedded 传输。这既便于测试你的代码,而又不需要创建大量的模拟(mock)对象。你的类将仍然符合常规的 API 事件流,保证该 ChannelHandler在和真实的传输一起使用时能够正确地工作。

应用程序的最佳传输建议:

应用程序的需求推荐的传输
非阻塞代码库或者一个常规的起点NIO(或者在 Linux 上使用 epoll)
阻塞代码库OIO
在同一个 JVM 内部的通信Local
测试 ChannelHandler 的实现Embedded

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

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

相关文章

chatgpt赋能Python-pycharm如何关联python

PyCharm如何关联Python 作为一款被广泛使用的Python集成开发环境&#xff08;IDE&#xff09;&#xff0c;PyCharm为Python程序员提供了丰富的开发工具和功能。在开始使用PyCharm之前&#xff0c;我们需要确保PyCharm已经正确地关联了Python。在本篇文章中&#xff0c;我们将介…

chatgpt赋能Python-pycharm访问网页

PyCharm访问网页的SEO技巧 PyCharm是一款强大且广受欢迎的Python开发环境&#xff0c;它提供了许多优秀的工具和功能&#xff0c;以便于Python项目的开发和管理。其中一个强大的功能是PyCharm可以访问网页&#xff0c;可以使你快速获取和分析数据。但是&#xff0c;在使用这个…

POSTGRESQL 通过TRIGGER 解决数据库表丢失数据的问题

开头还是介绍一下群&#xff0c;如果感兴趣polardb ,mongodb ,mysql ,postgresql ,redis 等有问题&#xff0c;有需求都可以加群群内有各大数据库行业大咖&#xff0c;CTO&#xff0c;可以解决你的问题。加群请联系 liuaustin3 &#xff0c;在新加的朋友会分到2群&#xff08;共…

Consul系列:什么是Consul?

引言 Consul 是 HashiCorp 公司推出的开源工具&#xff0c;用于实现分布式系统的服务发现与配置。与其他分布式服务注册与发现的方案&#xff0c; consu1 的方案更“一站式”&#xff0c;内置了服务注册 与发现框架、分布一致性协议实现、健康检查、Key/Value 存储、多数据中心…

多区域综合能源系统热网建模及系统运行优化(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

(转载)从0开始学matlab(第4天)—子数组

你可以选择和使用一个 MATLAB 函数的子集&#xff0c;好像他们是独立的数组一样。在数组名后面加括号&#xff0c;括号里面是所有要选择的元素的下标&#xff0c;这样就能选择这个函数的子集了。例如&#xff0c;假设定义了一个数组 arr1 如下 arr1[1.1 -2.2 3.3 -4.4 5.5] 那…

EMI超标问题排查

基于场外EMC测试反馈,EMI测试超标,测试结果如下图; 通过图形我们 可以看出,主要EMI超标频率集中在1GHz(大约700M的时候最差)附近。 使用RS频谱仪结合近场探头,进行复测: 复测情况确实存在EMI问题集中在700MHz左右。 去掉时钟输出接口时; 效果明显 通过试验,可以得出…

Java进阶-文件操作

1.File类 1.1File类概述和构造方法 File类介绍 它是文件和目录路径名的抽象表示文件和目录是可以通过File封装成对象的对于File而言,其封装的并不是一个真正存在的文件,仅仅是一个路径名而已.它可以是存在的,也可以是不存在的.将来是要通过具体的操作把这个路径的内容转换为具…

6.Redis事务

6.Redis事务 是什么&#xff1a;Redis事务VS数据库事务怎么玩Errors inside a transactionWatch监控&#xff1a;Optimistic locking using check-and-set 是什么&#xff1a; Redis Transactions allow the execution of a group of commands in a single step, they are cen…

系列二、Vue脚手架的基本语法

一、基于3.x版本脚手架创建vue项目的方式 1.1、基于交互式命令行的方式&#xff0c;创建vue项目 vue create project-name 1.2、基于图形化界面的方式&#xff0c;创建vue项目 vue ui 1.2.1、创建新项目 -详情 1.2.2、创建新项目-预设 1.2.3、创建新项目-功能 1.2.4、创建新…

千万不要告诉别人自己的绩效!某面试官觉得求职者表现不错,找熟人打听他离职原因,听说因为绩效低被劝退,决定不要他了!...

自己的绩效可以告诉别人吗&#xff1f; 一位网友提示&#xff1a; 告诉别人自己的绩效很恐怖&#xff01;在电梯里听到面试官说某求职者表现还不错&#xff0c;找熟人问了一下他上家公司的离职原因&#xff0c;听说是因为绩效低被劝退&#xff0c;所以不准备要他了。 网友说&am…

要不要和我们一起看看音视频技术未来的模样?

▲扫描图中二维码或点击“阅读原文” ▲ 直通LiveVideoStackCon 2023上海站九折优惠 2023年5月11日&#xff0c;Google I/O年度开发者大会完美落幕。随着PaLM2模型发布、Bard的自由访问&#xff0c;技术彻底迎来了“AI 时代”。 但今天我们想聊的并不是AI&#xff0c;而是一直支…

运用多输入模型优化不同维度特征

运用多输入模型优化不同维度特征 背景介绍 使用神经网络模型做用户付费金额预测&#xff0c;一种常见的特征工程场景&#xff0c;是把某个特定付费区间&#xff0c;比如付费金额大于10小于等于12的付费用户信息处理成特征&#xff0c;既可以将这部分用户的付费人数作为特征&a…

自助迁移工具升级!如何从 Confluence 切换至 ONES Wiki?

近日&#xff0c;ONES 升级了 Confluence 自助迁移工具&#xff0c;对迁移数据类型、迁移范围、迁移模式等多个维度的能力进行了提升&#xff0c;帮助企业更高效率、更低成本地将 Confluence 中的数据完整、准确地迁移至 ONES Wiki 中。 在 Confluence 与 ONES 服务资源充足的前…

国民游戏王者荣耀的真实地图开发之路

&#x1f449;腾小云导读 相信很多人都玩过王者荣耀&#xff0c;大家在欣赏其华丽的游戏界面以及炫酷的游戏技能时&#xff0c;是否好奇过王者荣耀的地图是怎样开发出来的&#xff1f;在开发的历程中&#xff0c;都有哪些问题&#xff1f;是怎样解决的&#xff1f;本文将从其地…

飞桨+文心一言的“动力装置”,藏着百度财报的增长密码

我们习以为常的科技世界&#xff0c;正在以肉眼可见的速度被大模型所改变甚至重构。不想错失机遇的科技企业&#xff0c;都怀揣着造AI重器的梦想&#xff0c;各种大模型纷至沓来。 发布大模型只是开始&#xff0c;如同火箭发射&#xff0c;升空是第一步&#xff0c;后续能否顺利…

【运维知识进阶篇】集群架构-Nginx反向代理详解

在互联网请求中&#xff0c;客户端通常无法直接向服务端发起请求&#xff0c;就需要用代理服务&#xff0c;来实现客户端和的交互&#xff0c;起到一个中介的作用。 Nginx代理服务常见模式 Nginx代理按照应用场景模式可以分为正向代理和反向代理。 正向代理是内部上网过程中&a…

将矩阵按指定对角线转化为一个下三角矩阵numpy.tril()方法

【小白从小学Python、C、Java】 【计算机等考500强证书考研】 【Python-数据分析】 将矩阵按指定对角线转化为一个下三角矩阵 numpy.tril() 选择题 关于以下代码说法错误的一项是? import numpy as np a np.array([[1,2],[3,4]]) print("【显示】a\n",a) print(&…

Nevron Open Vision for .NET 2023.1 Crack

Nevron Open Vision for .NET 2023.1 添加对 .NET Core 7.0 的支持以及用于图表控件的新 3D 渲染引擎。 2023 年 5 月 17 日 - 14:09 新版本 特征 一般改进 添加了对 Microsoft .NET 7.0 的支持- NOV 现在完全支持 .NET Core 7.0&#xff0c;此外还支持 Microsoft .NET Framewo…

chatgpt赋能Python-pycharm降低numpy版本

Pycharm中降低Numpy版本的步骤 如果你在使用Pycharm进行Python编程时遇到了Numpy版本不兼容的问题&#xff0c;你可能需要降低Numpy的版本。在这篇文章中&#xff0c;我们将介绍如何在Pycharm中降低Numpy版本&#xff0c;以便Python程序能够正常运行。 什么是Numpy&#xff1…