【Netty】Netty 概述(一)

news2025/2/23 6:18:22

文章目录

  • 前言
  • 一、Java原生API之痛
  • 二、Netty的优势
    • 2.1 非阻塞 I/O
    • 2.2 丰富的协议
    • 2.3 异步和事件驱动
    • 2.4 精心设计的API
    • 2.5 丰富的缓冲实现
    • 2.6 高效的网络传输
  • 三、Netty 核心概念
    • 3.1 核心组件
      • 3.1.1 事件模型
      • 3.1.2 字节缓冲区
      • 3.1.3 通信API
    • 3.2 传输服务
      • 3.2.1 NIO
      • 3.2.2 epoll
      • 3.2.3 OIO
      • 3.2.4 本地
      • 3.2.5 内嵌
    • 3.3 协议支持
  • 四、Netty简单应用
    • 4.1 引入Maven依赖
    • 4.2 服务端的管道处理器
    • 4.3 服务端主程序
    • 4.4 客户端管道处理器
    • 4.5 客户端主程序
    • 4.6 测试运行
  • 五、Netty 组件
    • 5.1 Channel
    • 5.2 EventLoop
    • 5.3 ChannelFuture
    • 5.4 ChannelHandler
    • 5.5 ChannelPipeline
    • 5.6 Bootstrap
    • 5.7 ByteBuf
  • 总结

前言

本篇文章我们就开始对 Netty来进行深入分析,首先我们来了解一下 JAVA NIO 、AIO的不足之处。

一、Java原生API之痛

虽然JAVA NIO 和 JAVA AIO框架提供了多路复用IO/异步IO的支持,但是并没有提供上层“信息格式”的良好封装。用这些API实现一款真正的网络应用则并非易事。

JAVA NIO 和 JAVA AIO并没有提供断连重连、网络闪断、半包读写、失败缓存、网络拥塞和异常码流等的处理,这些都需要开发者自己来补齐相关的工作。

AIO在实践中,并没有比NIO更好。AIO在不同的平台有不同的实现,windows系统下使用的是一种异步IO技术:IOCP;Linux下由于没有这种异步 IO 技术,所以使用的是epoll 对异步 IO 进行模拟。所以 AIO 在 Linux 下的性能并不理想。AIO 也没有提供对 UDP 的支持。

综上,在实际的大型互联网项目中,Java 原生的 API 应用并不广泛,取而代之的是一款第三方Java 框架,这就是Netty。

二、Netty的优势

Netty 提供 异步的、事件驱动的网络应用程序框架和工具,用以快速开发高性能、高可靠性的网络服务器和客户端程序。

2.1 非阻塞 I/O

Netty 是基于 Java NIO API 实现的网络应用框架,使用它可以快速简单的开发网络应用程序,如服务器和客户端程序。Netty 大大简化了网络程序开发的过程,如 TCP 和 UDP 的 Socket 服务的开发。

由于是基于 NIO 的 API,因此,Netty 可以提供非阻塞的 I/O操作,极大的提升了性能。同时,Netty 内部封装了 Java NIO API 的复杂性,并提供了线程池的处理,使得开发 NIO 的应用变得极其简单。

2.2 丰富的协议

Netty 提供了简单、易用的 API ,但这并不意味着应用程序会有难维护和性能低的问题。Netty 是一个精心设计的框架,它从许多协议的实现中吸收了很多的经验,如 FTP 、SMTP、 HTTP、许多二进制和基于文本的传统协议。

Netty 支持丰富的网络协议,如TCP、 UDP、 HTTP、 HTTP/2、 WebSocket、 SSL/TLS等,这些协议实现开箱即用,因此,Netty 开发者能够在不失灵活的前提下来实现开发的简易性、高性能和稳定性。

2.3 异步和事件驱动

Netty 是异步事件驱动的框架,该框架体现为所有的I/O操作都是异步的,所有的I/O调用会立即返回,并不保证调用成功与否,但是调用会返回ChannelFuture。Netty 会通过 ChannelFuture通知调用是成功了还是失败了,抑或是取消了。

同时,Netty 是基于事件驱动的,调用者并不能立即获得结果,而是通过事件监听机制,用户可以方便地主动获取或者通过通知机制获得I/O操作的结果。

当Future对象刚刚创建时,处于非完成状态,调用者可以通过返回的ChannelFuture来获取操作执行的状态,再通过注册监听函数来执行完成后的操作,常见有如下操作:

  • 通过isDone方法来判断当前操作是否完成。
  • 通过isSuccess方法来判断已完成的当前操作是否成功。
  • 通过getCause方法来获取已完成的当前操作失败的原因。
  • 通过isCancelled方法来判断已完成的当前操作是否被取消。
  • 通过addListener方法来注册监听器,当操作已完成(isDone方法返回完成),将会通知指定的监听器;如果future对象已完成,则理解通知指定的监听器。

例如:下面的代码中绑定端口是异步操作,当绑定操作处理完,将会调用相应的监听器处理逻辑。

serverBootstrap.bind(port).addListener(future -> {
    if(future.isSuccess()){
        System.out.println("端口绑定成功!");
    }else {
        System.out.println("端口绑定失败!");
    }
});

相比传统的阻塞 I/O,Netty 异步处理的好处是不会造成线程阻塞,线程在 I/O操作期间可以执行其他的程序,在高并发情形下会更稳定并拥有更高的吞吐量。

2.4 精心设计的API

Netty 从开始就为用户提供了体验最好的API及实现设计。

例如,在用户数较小的时候可能会选择传统的阻塞API,毕竟与 Java NIO 相比使用阻塞 API 将会更加容易一些。然而,当业务量呈指数增长并且服务器需要同时处理成千上万的客户连接,便会遇到问题。这种情况下可能会尝试使用 Java NIO,但是复杂的 NIO Selector 编程接口又会耗费大量的时间并最终会阻碍快速开发。

Netty 提供了一个叫作 channel的统一的异步I/O编程接口,这个编程接口抽象了所有点对点的通信操作。也就是说,如果应用是基于Netty 的某一种传输实现,那么同样的,应用也可以运行在 Netty 的另一种传输实现上。Channel常见的子接口有:
在这里插入图片描述

2.5 丰富的缓冲实现

Netty 使用自建的缓存 API,而不是使用 Java NIO 的 ByteBuffer 来表示一个连续的字节序列。与 ByteBuffer 相比,这种方式拥有明显的优势。

Netty 使用新的缓冲类型 ByteBuf ,并且被设计为可从底层解决 ByteBuffer 问题,同时还满足日常网络应用开发需要的缓冲类型。

Netty 重要有以下特性:

  • 允许使用自定义的缓冲类型。
  • 复合缓冲类型中内置透明的零拷贝实现。
  • 开箱即用动态缓冲类型,具有像 StringBuffer 一样的动态缓冲能力。
  • 不再需要调用flip()方法。
  • 正常情况下具有比ByteBuffer更快的响应速度。

2.6 高效的网络传输

Java 原生的序列化主要存在以下几个弊端:

  • 无法跨语言。

  • 序列化后码流太大。

  • 序列化后性能太低。

业界有非常多的框架用于解决上述问题,如 Google Protobuf 、JBoss Marshalling、Facebook Thrift等。针对这些框架,Netty 都提供了相应的包将这些框架集成到应用中。同时,Netty 本身也提供了众多的编解码工具,方便开发者使用。开发者可以基于 Netty 来开发高效的网络传输应用,例如:高性能的消息中间件 Apache RocketMQ、高性能RPC框架Apache Dubbo等。

三、Netty 核心概念

在这里插入图片描述

从上述的架构图可以看出,Netty 主要由三大块组成:

  • 核心组件
  • 传输服务
  • 协议

3.1 核心组件

核心组件包括:事件模型、字节缓冲区和通信API

3.1.1 事件模型

Netty 是基于异步事件驱动的,该框架体现为所有的I/O操作都是异步的,调用者并不能立即获得结果,而是通过事件监听机制,用户可以方便地主动获取或者通过通知机制获得I/O操作的结果。
Netty 将所有的事件按照它们与入站或出站数据流的相关性进行了分类。
可能由入站数据或者相关的状态更改而触发的事件包括以下几项:

  • 连接已被激活或者连接失活。
  • 数据读取。
  • 用户事件。
  • 错误事件。

出站事件是未来将会触发的某个动作的操作结果,包括以下动作:

  • 打开或者关闭到远程节点的连接。
  • 将数据写到或者冲刷到套接字。

每个事件都可以被分发到ChannelHandler类中的某个用户实现的方法。

3.1.2 字节缓冲区

Netty 使用了区别于Java ByteBuffer 的新的缓冲类型ByteBuf,ByteBuf提供了丰富的特性。

3.1.3 通信API

Netty 的通信API都被抽象到Channel里,以统一的异步I/O编程接口来满足所有点对点的通信操作。

3.2 传输服务

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

3.2.1 NIO

io.netty.channel.socket.nio包用于支持NIO。该包下面的实现是使用java.nio.channels包作为基础(基于选择器的方式)。

3.2.2 epoll

io.netty.channel.epoll包用于支持由 JNI 驱动的 epoll 和 非阻塞 IO。
需要注意的是,这个epoll传输只能在 Linux 上获得支持。epoll同时提供多种特性,如:SO_REUSEPORT 等,比 NIO传输更快,而且是完全非阻塞的。

3.2.3 OIO

io.netty.channel.socket.oio包用于支持使用java.net包作为基础的阻塞I/O。

3.2.4 本地

io.netty.channel.local包用于支持在 VM 内部通过管道进行通信的本地传输。

3.2.5 内嵌

io.netty.channel.embedded包作为内嵌传输,允许使用ChannelHandler而又不需要一个真正的基于网络的传输。

3.3 协议支持

Netty 支持丰富的网络协议,如TCP、 UDP、 HTTP、 HTTP/2、 WebSocket、 SSL/TLS等,这些协议实现开箱即用,因此,Netty 开发者能够在不失灵活的前提下来实现开发的简易性、高性能和稳定性。

四、Netty简单应用

4.1 引入Maven依赖

<dependency>
    <groupId>io.netty</groupId>
    <artifactId>netty-all</artifactId>
    <version>4.1.49.Final</version>
</dependency>

4.2 服务端的管道处理器

public class NettyServerHandler extends ChannelInboundHandlerAdapter {

    //读取数据实际(这里我们可以读取客户端发送的消息)
    /*
    1. ChannelHandlerContext ctx:上下文对象, 含有 管道pipeline , 通道channel, 地址
    2. Object msg: 就是客户端发送的数据 默认Object
     */
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        System.out.println("server ctx =" + ctx);
        Channel channel = ctx.channel();
        //将 msg 转成一个 ByteBuf
        //ByteBuf 是 Netty 提供的,不是 NIO 的 ByteBuffer.
        ByteBuf buf = (ByteBuf) msg;
        System.out.println("客户端发送消息是:" + buf.toString(CharsetUtil.UTF_8));
        System.out.println("客户端地址:" + channel.remoteAddress());
    }


    //数据读取完毕
    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
        //writeAndFlush 是 write + flush
        //将数据写入到缓存,并刷新
        //一般讲,我们对这个发送的数据进行编码
        ctx.writeAndFlush(Unpooled.copiedBuffer("公司最近账户没啥钱,再等几天吧!", CharsetUtil.UTF_8));
    }

    //处理异常, 一般是需要关闭通道
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        ctx.close();
    }
}

NettyServerHandler继承自ChannelInboundHandlerAdapter,这个类实现了ChannelInboundHandler接口。ChannelInboundHandler提供了许多事件处理的接口方法。

这里覆盖了channelRead()事件处理方法。每当从客户端收到新的数据时,这个方法会在收到消息时被调用。

channelReadComplete()事件处理方法是数据读取完毕时被调用,通过调用ChannelHandlerContext的writeAndFlush()方法,把消息写入管道,并最终发送给客户端。

exceptionCaught()事件处理方法是,当出现Throwable对象时才会被调用。

4.3 服务端主程序

public class NettyServer {

    public static void main(String[] args) throws Exception {
        //创建BossGroup 和 WorkerGroup
        //说明
        //1. 创建两个线程组 bossGroup 和 workerGroup
        //2. bossGroup 只是处理连接请求 , 真正的和客户端业务处理,会交给 workerGroup完成
        //3. 两个都是无限循环
        //4. bossGroup 和 workerGroup 含有的子线程(NioEventLoop)的个数
        //   默认实际 cpu核数 * 2
        //
        EventLoopGroup bossGroup = new NioEventLoopGroup(1);
        EventLoopGroup workerGroup = new NioEventLoopGroup(); //8
        try {
            //创建服务器端的启动对象,配置参数
            ServerBootstrap bootstrap = new ServerBootstrap();
            //使用链式编程来进行设置
            bootstrap.group(bossGroup, workerGroup) //设置两个线程组
                    .channel(NioServerSocketChannel.class) //bossGroup使用NioSocketChannel 作为服务器的通道实现
                    .option(ChannelOption.SO_BACKLOG, 128) // 设置线程队列得到连接个数 option主要是针对boss线程组,
                    .childOption(ChannelOption.SO_KEEPALIVE, true) //设置保持活动连接状态 child主要是针对worker线程组
                    .childHandler(new ChannelInitializer<SocketChannel>() {//workerGroup使用 SocketChannel创建一个通道初始化对象																														(匿名对象)
                        //给pipeline 设置处理器
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
                            //可以使用一个集合管理 SocketChannel, 再推送消息时,可以将业务加入到各个channel 对应的 NIOEventLoop 的 									taskQueue 或者 scheduleTaskQueue
                            ch.pipeline().addLast(new NettyServerHandler());
                        }
                    }); // 给我们的workerGroup 的 EventLoop 对应的管道设置处理器

            System.out.println(".....服务器 is ready...");
            //绑定一个端口并且同步, 生成了一个 ChannelFuture 对象
            //启动服务器(并绑定端口)
            ChannelFuture cf = bootstrap.bind(7788).sync();
            //给cf 注册监听器,监控我们关心的事件
            cf.addListener(new ChannelFutureListener() {
                @Override
                public void operationComplete(ChannelFuture future) throws Exception {
                    if (cf.isSuccess()) {
                        System.out.println("服务已启动,端口号为7788...");
                    } else {
                        System.out.println("服务启动失败...");
                    }
                }
            });
            //对关闭通道进行监听
            cf.channel().closeFuture().sync();
        } finally {
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }
}

NioEventLoopGroup是用来处理I/O操作的多线程事件循环器。Netty 提供了许多不同的EventLoopGroup的实现来处理不同的传输。

上面的服务端应用中,有两个NioEventLoopGroup被使用。第一个叫作bossGroup,用来接收进来的连接。第二个叫作workerGroup,用来处理已经被接收的连接,一旦 bossGroup接收连接,就会把连接的信息注册到workerGroup上。

ServerBootstrap是一个NIO服务的引导启动类。可以在这个服务中直接使用Channel。

  • group方法用于 设置EventLoopGroup。
  • 通过Channel方法,可以指定新连接进来的Channel类型为NioServerSocketChannel类。
  • childHandler用于指定ChannelHandler,也就是前面实现的NettyServerHandler。
  • 可以通过option设置指定的Channel来实现NioServerSocketChannel的配置参数。
  • childOption主要设置SocketChannel的子Channel的选项。
  • bind用于绑定端口启动服务。

4.4 客户端管道处理器

public class NettyClientHandler extends ChannelInboundHandlerAdapter {

    //当通道就绪就会触发该方法
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        System.out.println("client ctx =" + ctx);
        ctx.writeAndFlush(Unpooled.copiedBuffer("老板,工资什么时候发给我啊?", CharsetUtil.UTF_8));
    }

    //当通道有读取事件时,会触发
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        ByteBuf buf = (ByteBuf) msg;
        System.out.println("服务器回复的消息:" + buf.toString(CharsetUtil.UTF_8));
        System.out.println("服务器的地址: "+ ctx.channel().remoteAddress());
    }

    //处理异常, 一般是需要关闭通道
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        cause.printStackTrace();
        ctx.close();
    }
}

channelRead方法中将接收到的消息转化为字符串,方便在控制台上打印出来。
channelRead接收到的消息类型为ByteBuf,ByteBuf提供了转为字符串的方便方法。

4.5 客户端主程序

public class NettyClient {

    public static void main(String[] args) throws Exception {
        //客户端需要一个事件循环组
        EventLoopGroup group = new NioEventLoopGroup();
        try {
            //创建客户端启动对象
            //注意客户端使用的不是 ServerBootstrap 而是 Bootstrap
            Bootstrap bootstrap = new Bootstrap();
            //设置相关参数
            bootstrap.group(group) //设置线程组
                    .channel(NioSocketChannel.class) // 设置客户端通道的实现类(反射)
                    .handler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
                            ch.pipeline().addLast(new NettyClientHandler()); //加入自己的处理器
                        }
                    });
            System.out.println("客户端 ok..");
            //启动客户端去连接服务器端
            //关于 ChannelFuture 要分析,涉及到netty的异步模型
            ChannelFuture channelFuture = bootstrap.connect("127.0.0.1", 7788).sync();
            //给关闭通道进行监听
            channelFuture.channel().closeFuture().sync();
        } finally {
            group.shutdownGracefully();
        }
    }
}

客户端只需要一个NioEventLoopGroup就可以了。

4.6 测试运行

分别启动服务器 NettyServer 和客户端 NettyClient程序
服务端控制台输出内容:

....服务器 is ready...
服务已启动,端口号为7788...
server ctx =ChannelHandlerContext(NettyServerHandler#0, [id: 0xa1b2233c, L:/127.0.0.1:7788 - R:/127.0.0.1:63239])
客户端发送消息是:老板,工资什么时候发给我啊?
客户端地址:/127.0.0.1:63239

客户端控制台输出内容:

客户端 ok..
client ctx =ChannelHandlerContext(NettyClientHandler#0, [id: 0x21d6f98e, L:/127.0.0.1:63239 - R:/127.0.0.1:7788])
服务器回复的消息:公司最近账户没啥钱,再等几天吧!
服务器的地址: /127.0.0.1:7788

至此,一个简单的基于Netty开发的服务端和客户端就完成了。

五、Netty 组件

这里我们先简要了解一下以下几个组件的作用,留个映像,后面我们会对每个组件做详细深入。

5.1 Channel

同我们前面学习 Java NIO Channel 类似,Netty Channel 在此基础上做了高度抽象的封装,主要用于网络 I/O 数据的基本操作,如 bind ()、connect ()、read ()、write () 等。

5.2 EventLoop

在网络连接的整个生命周期内,发生的所有事件的处理主要有 EventLoop 来处理

5.3 ChannelFuture

在 Netty 中,I/O 操作主要都是异步进行,当操作发生时,我们需要通过一种方式来知道操作在未来的时间点的执行结果。ChannelFutrue 中的 addListener () 方法,可以注册监听器 ChannelFutureListener,当操作完成时,监听器可以主动通知我们。

5.4 ChannelHandler

channelHandler 主要用于应用程序中的业务逻辑的处理,网络中的进入与出去的数据都经由它处理,当有事件发生时,channelHandler 会被触发执行。

5.5 ChannelPipeline

ChannelPipeline 提供了一种容器,用于定义数据流入与流出过程中的处理流程。可以将 Pipeline 看作是一条流水线,原始的原料 (字节流) 进来,经过加工,最后输出。

5.6 Bootstrap

主要用于配置服务端或客户端的 Netty 程序的启动信息。

5.7 ByteBuf

字节数据容器,提供比 Java NIO ByteBuffer更好的的 API。

总结

本篇文章主要讲解了 Netty 产生的背景、特点、核心组件及如何快速开启第一个 Netty 应用。

后面我们会分析Netty架构设计、Channel、ChannelHandler、字节缓冲区ByteBuf、线程模型、编解码、引导程序等方面的知识。

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

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

相关文章

让数据背后的那些话创造价值 | 数据增长

从行业背景而言&#xff0c;流量红利逐渐消失&#xff0c;野蛮生长的互联网时代接近尾声。传统的烧钱模式、靠体力投放的形式日渐乏力。但是&#xff0c;企业总是要追求增长的。所以在行业大背景下&#xff0c;依靠技术和数据的力量寻求更科学、更高效的方法达成营销目标&#…

Windows系统数据结构——最小生成树、Prim算法和Kruskal算法

我是荔园微风&#xff0c;作为一名在IT界整整25年的老兵&#xff0c;今天总结一下Windows系统数据结构——最小生成树、Prim算法和Kruskal算法。 我在各在论坛看了很多相关帖子&#xff0c;发现一个简单的问题都被复杂化了。最小生成树、Prim算法和Kruskal算法真的没有大家想的…

【JavaSE】Java基础语法(五):数组详解

文章目录 &#x1f378;1.1 数组介绍&#x1f378;1.2 数组的动态初始化1.2.1 什么是动态初始化1.2.2 动态初始化格式&#x1f378;1.3 数组元素访问1.3.1 什么是索引1.3.2 访问数组元素格式1.3.3 示例代码 &#x1f378;1.4 内存分配1.4.1 内存概述1.4.2 java中的内存分配 &am…

Ubuntu crontab 遇到的sh脚本一些问题(手动执行可以,自动执行不行)

问题一&#xff1a; 问题描述&#xff1a; 在写一个脚本循环时候&#xff0c;出现“let:not found”,这是因为在ubuntu默认是指向bin/dash解释器的,dash是阉割版的bash,其功能远没有bash强大和丰富.并且dash不支持let和i等功能. 解决办法&#xff1a; 打开一个终端输入&#xf…

springboot基于Java的校园二手物品交易平台jspm9qw4i

本基于Java的校园二手物品交易平台采用Java语言和Jsp技术&#xff0c;框架采用SPRINGBOOT&#xff0c;搭配Mysql数据库&#xff0c;运行在Idea里。本系统针对校园二手商品的交易而开发&#xff0c;提供管理员、学生、学生二手三种角色的服务。总的功能包括商品的查询、商品的购…

基于html+css的图展示89

准备项目 项目开发工具 Visual Studio Code 1.44.2 版本: 1.44.2 提交: ff915844119ce9485abfe8aa9076ec76b5300ddd 日期: 2020-04-16T16:36:23.138Z Electron: 7.1.11 Chrome: 78.0.3904.130 Node.js: 12.8.1 V8: 7.8.279.23-electron.0 OS: Windows_NT x64 10.0.19044 项目…

【六袆 - Redis】Redis内存数据库;redis数据结构;redis文档

Redis 关于redis 官方文档&#xff1a; https://redis.io/docs/about/ https://redis.com/redis-enterprise/data-structures/ 关于redis Redis: 是一个开源&#xff08;BSD 许可&#xff09;内存数据结构存储&#xff0c;用作数据库、缓存、消息代理和流引擎。Redis提供数据结…

CNVD - 5000w通用产品的收集方法

本文转载于&#xff1a;https://mp.weixin.qq.com/s?__bizMzg5OTY2NjUxMw&mid2247507214&idx1&sn0e6df46ee930cb35ab0650867cef8af5&chksmc04d5a30f73ad3261a6fa6a8cb8c4ddc4ee8fac2a58f495c05030adc2d27e3ead65264f24f75&mpshare1&scene23&srcid…

MySQL业务并发减数量,数量未减

业务背景 最近在折腾老系统&#xff0c;折腾了好久&#xff0c;发现一个数据库问题&#xff0c;用户点赞数量&#xff0c;如果用户取消点赞情况下&#xff0c;正常情况10次取消数据库都返回成功&#xff0c;但其中有2次没有取消。 数据库场景 在MySQL中看下面一个场景。 业务…

jQuery操作练习-隔行变色

<!DOCTYPE HTML> <html> <head> <meta http-equiv"Content-Type" content"text/html; charsetUTF-8"> <title>jQuery操作练习-隔行变色</title> <script type"text/javascript&q…

越来越好玩,用ChatGPT+Python 做有声小说!

菜鸟学Python-第623篇原创 现在我们几百人的会员群已经玩的越来越高级了&#xff0c;利用chatgpt花色玩法&#xff01;有玩百度问一问每天早上6点多起来抢单的&#xff0c;有玩微信机器人帮人部署接单的&#xff0c;也有玩咸鱼去给大学生指导论文的&#xff01; 利用chatgpt4玩…

USB设备连接和枚举

https://space.bilibili.com/489340606/channel/collectiondetail?sid896957 以下图片来自于沁恒微电子蔡亮工程师的讲课&#xff0c;对USB开发入门很有好处。 1. USB主设备和从设备 2. USB设备按功能分类 3. USB功能设备内部架构 可以有多个配置&#xff0c;但同一个时间只…

华为OD机试真题 Java 实现【字母组合】【2023Q1 200分】

一、题目描述 每个数字对应多个字母&#xff0c;对应关系如下&#xff1a; 0&#xff1a;a,b,c 1&#xff1a;d,e,f 2&#xff1a;g,h,i 3&#xff1a;j,k,l 4&#xff1a;m,n,o 5&#xff1a;p,q,r 6&#xff1a;s,t 7&#xff1a;u,v 8&#xff1a;w,x 9&#xff1a;y, z …

PostGIS的10个最佳实践

PostGIS 是一个功能强大的开源空间数据库&#xff0c;可用于存储、查询和分析地理空间数据。 对于需要存储和分析大量地理空间数据的组织来说&#xff0c;这是一个流行的选择。 但是&#xff0c;正确使用 PostGIS 以充分利用它很重要。 在本文中&#xff0c;我们将讨论 10 个 …

Mit6.006-lecture09-Breadth-First-Search

一、新单元&#xff1a;图 Quiz 1包含lecture01到lecture08&#xff0c;关注数据结构和排序 今天开始新单元&#xff0c;lecture09-lecture14&#xff0c;关注图算法 二、图应用 图无处不在 任何网络系统都存在有向连接图 比如&#xff1a;路网、计算机网络、社交网络 任…

1146 Topological Order(31行代码+详细注释)

分数 25 全屏浏览题目 作者 CHEN, Yue 单位 浙江大学 This is a problem given in the Graduate Entrance Exam in 2018: Which of the following is NOT a topological order obtained from the given directed graph? Now you are supposed to write a program to test …

Elasticsearch:在 Elastic 中访问机器学习模型

作者&#xff1a;Bernhard Suhm, Josh Devins Elastic 支持你需要的机器学习模型 Elastic 让你可以应用适合你的用例和 ML 专业水平的机器学习 (ML)。 你有多种选择&#xff1a; 利用内置的模型。 除了我们的可观察性和安全解决方案中针对特定安全威胁和系统问题类型的模型外…

Ubuntu crontab定时任务

1. crontab 相关的命令&#xff1a; 安装&#xff1a;apt-get install cron 启动&#xff1a;service cron start 重启&#xff1a;service cron restart 停止&#xff1a;service cron stop 检查状态&#xff1a;service cron status 查询cron可用的命令&#xff1a;service …

【算法排序】直接插入排序

目录 一、概念及其介绍二、过程图示三、复杂度以及稳定性四、代码实现 一、概念及其介绍 插入排序(InsertionSort)&#xff0c;一般也被称为直接插入排序。 对于少量元素的排序&#xff0c;它是一个有效的算法。插入排序是一种最简单的排序方法&#xff0c;它的基本思想是将一…

Keil(MDK-ARM)如何补充安装旧的编译器 AC5(ARM Compiler 5)

目录 一、前言二、下载1. 进入 Arm Developer 官网2. 下载 ARM Compiler 5 安装包3. 下载完成 三、安装1. 开始安装2. 安装过程3. 安装完成 四、配置1. 打开“Manage Project Items”2. 添加 ARM Compiler 5 编译器3. 添加成功4. 选择 ARM Compiler 5 作为当前使用的编译器 一、…