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

面试题(一)

1、对 netty 的理解?比如说 netty 是怎么去处理?(java)

Netty 是一个基于 Java 的高性能、异步事件驱动的网络应用框架,广泛用于处理高并发和低延迟的网络通信需求。它在处理网络请求时,利用了 NIO(New I/O)的非阻塞机制,适合构建高效的网络应用,如 HTTP 服务、WebSocket 服务和自定义协议。

Netty 的核心概念与处理机制

  1. 异步和事件驱动:Netty 基于事件驱动的架构,使用 Reactor 模型处理网络请求。它将 I/O 操作抽象为事件,并通过事件循环机制在不同阶段处理事件。Netty 的 Channel 类代表网络连接,所有 I/O 操作都与 Channel 相关。通过注册监听的事件,Netty 使用回调机制异步处理读写操作,避免阻塞线程。

  2. 多线程模型与事件循环(EventLoop):Netty 使用多线程模型进行网络事件的处理,核心是 EventLoop,它是一个单线程事件循环,用来处理注册到它上的 Channel 的所有 I/O 操作。在高并发场景中,Netty 会通过多组 EventLoop(通常是 NioEventLoop)和线程池来分发请求,提高系统的并发性能。

  3. ChannelPipeline 与 Handler 链:每个 Channel 都会关联一个 ChannelPipeline,里面包含一组 ChannelHandler。这些 Handler 是用于处理不同阶段的 I/O 事件的逻辑单元。比如:

    • ChannelInboundHandler 处理入站事件(如连接、数据读取等)。
    • ChannelOutboundHandler 处理出站事件(如数据写入、连接关闭等)。
      这些 Handler 是按顺序被执行的,每个 Handler 可以处理特定的 I/O 事件。
  4. 零拷贝(Zero Copy)机制:Netty 在处理数据时,尽可能避免不必要的数据复制。例如,Netty 提供的 ByteBuf 数据结构支持直接缓冲区和组合缓冲区,这些优化减少了数据在内存中的复制次数,从而提高了数据处理的效率。

  5. 内存管理与对象池化:Netty 通过内存池技术来管理 ByteBuf,这可以减少频繁分配和释放内存带来的开销。它会复用对象,提升性能。

  6. 支持多种协议与扩展性:Netty 提供了多种内置的协议支持(如 HTTP、WebSocket、SSL/TLS),并且它允许开发者自定义协议。Netty 的 Codec 组件(包括解码器和编码器)负责在不同协议之间转换消息格式,比如从字节流到业务对象的转换。

Netty 的典型处理流程

一个典型的 Netty 网络应用会通过以下几个步骤处理请求:

  1. Bootstrap 启动:使用 ServerBootstrap 创建服务端应用。通过 NioEventLoopGroup 来指定负责处理连接的线程池。
  2. ChannelPipeline 和 Handler 设置:将各种自定义的 ChannelHandler 添加到 ChannelPipeline,设置入站和出站的处理逻辑。
  3. 接收连接并处理请求:通过 Reactor 模型,Netty 会异步监听来自客户端的请求,连接建立后,事件(如 channelRead)会被触发,并通过事件循环机制调用相应的 Handler 来处理业务逻辑。
  4. 返回响应:处理完成后,结果通过出站 Handler 返回给客户端。

Netty 的优势

  1. 高并发和低延迟:Netty 基于 NIO 非阻塞的模型,能在高并发环境下保持良好的性能表现。
  2. 异步与事件驱动:Netty 的异步架构降低了线程阻塞的开销,能更高效地处理 I/O 密集型任务。
  3. 内置优化机制:通过零拷贝、内存池等优化技术,Netty 大幅减少了内存和 CPU 的开销。
  4. 灵活性与可扩展性:支持多种协议的扩展,开发者可以根据需要自定义协议和 Handler 来处理特定业务需求。

总结

Netty 是一个高效、灵活、异步的网络框架,它通过事件驱动模型、非阻塞 I/O 和强大的内存管理,提供了在高并发场景下的卓越表现。Netty 适用于各种网络应用场景,并广泛用于高性能、分布式系统的开发中。

2、IO 多路复用能大概深入讲下?

I/O 多路复用(I/O multiplexing)是一种通过单个线程或进程来监视多个 I/O 事件的技术,广泛用于网络编程中处理并发连接的场景。它的核心思想是让一个线程可以同时监控多个 I/O 通道(如文件描述符、socket),并且只有当某个通道准备好读写操作时才进行相应的处理。这种方式极大地提高了服务器处理并发连接的效率,并减少了资源消耗。

为什么需要 I/O 多路复用?

通常,在传统的 I/O 操作中,程序会等待一个 I/O 操作完成(例如从 socket 读取数据),这被称为阻塞 I/O。当并发连接很多时,阻塞 I/O 会导致系统资源(例如线程)浪费,无法高效地处理大量并发连接。而通过 I/O 多路复用,程序可以监听多个 I/O 事件,当任何一个事件就绪时才进行相应的处理,从而避免了阻塞。

I/O 多路复用的几种常见方法

  1. select

    • select 是最早的 I/O 多路复用机制之一。它允许你监视多个文件描述符(fd),并在其中任何一个文件描述符变为可读、可写或有异常时通知你。
    • 缺点select 的文件描述符集(fd set)有一个上限(通常是 1024 或 2048),而且每次调用都需要重新构建 fd 集,并线性扫描每一个 fd,这会导致在大量并发连接的情况下性能下降。
  2. poll

    • pollselect 的改进版本,突破了文件描述符的上限限制。与 select 类似,它也会阻塞进程,直到有文件描述符准备就绪。
    • 缺点:虽然解决了 fd 集合大小的限制,但 poll 每次仍然需要遍历所有的 fd,当有大量文件描述符时性能不佳。
  3. epoll

    • epoll 是 Linux 中对 selectpoll 的改进,特别适合大量并发连接的场景。epoll 不需要在每次等待时遍历所有的文件描述符,而是通过事件驱动机制来通知哪些文件描述符准备好了。
    • 特性
      • 水平触发(LT,Level-triggered):类似于 selectpoll,只要文件描述符准备就绪,系统会一直通知。
      • 边缘触发(ET,Edge-triggered):当文件描述符的状态从未准备好变为准备好时,只会通知一次。通常使用这个模式需要使用非阻塞 I/O。
    • 优势epoll 使用了事件通知机制和内核数据结构,避免了每次调用时重新传递所有的文件描述符,大幅提升了性能,尤其适合处理成千上万的连接。
  4. kqueue

    • 这是在 BSD 系统中类似于 epoll 的 I/O 多路复用机制,具有高效的事件通知机制。

I/O 多路复用的工作原理

I/O 多路复用的核心在于将多个文件描述符注册到一个 I/O 监视器(如 selectpollepoll 等),当任意文件描述符的状态发生变化(例如可读、可写),监视器会返回并通知应用程序。这时,程序可以执行具体的读写操作。

epoll 为例,其基本工作流程如下:

  1. 创建 epoll 实例:通过 epoll_create() 创建一个 epoll 实例。
  2. 注册文件描述符:将需要监控的文件描述符添加到 epoll 实例中,通过 epoll_ctl() 进行控制,指定需要监控的事件(例如可读、可写)。
  3. 等待事件:通过 epoll_wait() 等待事件发生。当某个文件描述符状态发生变化时,epoll 会返回这个文件描述符,应用程序可以对其进行处理。
  4. 处理事件:处理完事件后,可以选择重新监视或者移除文件描述符。

I/O 多路复用的优缺点

优点

  • 减少线程开销:使用 I/O 多路复用时,通常只需要一个线程处理所有 I/O 事件,而不必为每个连接创建单独的线程。
  • 高效处理并发:尤其是 epoll,在高并发环境下可以避免大量的 CPU 开销和上下文切换。
  • 灵活性:可以在单个进程或线程中处理多个 I/O 操作。

缺点

  • 复杂性增加:程序设计需要考虑事件驱动的模型,通常要编写复杂的状态机来处理非阻塞的 I/O 操作。
  • 边缘触发的复杂性:使用 epoll 的边缘触发时,程序需要仔细处理可能的 race condition,确保所有数据被正确读取。

典型应用场景

  1. 高并发网络服务器:如 HTTP 服务器、代理服务器、WebSocket 服务等。在这种场景下,I/O 多路复用可以极大地提升性能,减少资源消耗。
  2. 异步 I/O 处理:例如消息队列、数据库代理等,使用 I/O 多路复用可以避免频繁的线程创建和销毁。
  3. 图形用户界面程序:在 GUI 程序中,使用 I/O 多路复用可以监听用户输入、网络通信、文件 I/O 等多个事件,并在事件发生时及时更新界面。

总结

I/O 多路复用是一种非常高效的并发处理机制,尤其是在需要同时处理大量网络连接的场景中,能有效提升系统的吞吐量和响应能力。随着 epollkqueue 等高效机制的引入,I/O 多路复用已经成为现代服务器开发的关键技术之一。

3、.epoll怎么判断文件描述符就绪了?(epoll_wait)

epoll 通过内核事件驱动机制判断文件描述符的就绪状态。它不需要反复轮询文件描述符,而是通过注册和等待事件的方式来检测文件描述符是否可以进行 I/O 操作。以下是 epoll 判定文件描述符就绪的基本流程:

  1. 创建 epoll 实例
    使用 epoll_create() 创建一个 epoll 实例。这个实例会被用来管理和监视文件描述符的事件。

  2. 注册文件描述符到 epoll
    使用 epoll_ctl() 将文件描述符添加到 epoll 实例中,并指定要监视的事件(如可读、可写或错误)。常用的事件类型包括:

EPOLLIN:文件描述符可读。
EPOLLOUT:文件描述符可写。
EPOLLERR:发生错误。
EPOLLET:边缘触发模式(Edge Triggered)。
3. 等待文件描述符事件
调用 epoll_wait(),使得进程进入阻塞状态,直到有注册的文件描述符上发生事件。这时,epoll_wait() 会返回一个事件数组,表示哪些文件描述符已经就绪(即有 I/O 事件发生)。内核会根据注册的事件类型进行判定,通常是以下两种情况:

可读事件(EPOLLIN):表示文件描述符的数据已经准备好可以读取,或者客户端已经关闭连接。
可写事件(EPOLLOUT):表示可以向文件描述符写入数据。
4. 事件处理
当 epoll_wait() 返回时,它会告诉应用程序哪些文件描述符有 I/O 事件发生。这时,程序就可以对就绪的文件描述符进行相应的读写操作。

  1. 水平触发(LT) vs 边缘触发(ET)
    水平触发(LT, Level Triggered):只要文件描述符有未完成的 I/O 事件(比如数据未完全读出),每次调用 epoll_wait() 都会返回该文件描述符。适合于阻塞 I/O 模式。
    边缘触发(ET, Edge Triggered):当文件描述符的状态从未准备好变为准备好时,epoll 只会通知一次,适合于非阻塞 I/O 模式。使用 ET 模式时需要确保每次处理时将数据完全读出或写入,否则可能会错过事件。

epoll 的高效性
epoll 使用一个事件驱动的回调机制,在内核中维护一个红黑树结构来高效地管理和查询文件描述符,这使得它在处理大量并发连接时,能够快速找到就绪的文件描述符。

通过事件通知和就绪文件描述符的内核管理,epoll 能够高效处理大量文件描述符,尤其是在高并发的网络服务中。

struct epoll_event ev, events[MAX_EVENTS];
int epollfd = epoll_create1(0);
ev.events = EPOLLIN;  // 监控可读事件
ev.data.fd = sockfd;  // 需要监控的文件描述符// 添加文件描述符到 epoll 实例中
epoll_ctl(epollfd, EPOLL_CTL_ADD, sockfd, &ev);// 等待事件发生
int nfds = epoll_wait(epollfd, events, MAX_EVENTS, -1);//等待所监控文件描述符上所有事件的产生/*参数说明epfd:epoll 文件描述符,通过 epoll_create 或 epoll_create1 创建。events:指向 epoll_event 结构体数组的指针,调用成功后,系统会将就绪的事件(epfd)放置(复制)在该数组中。maxevents:数组 events 的大小,指定 epoll_wait 函数能返回的最大事件数。timeout:超时时间,单位是毫秒。如果设置为 -1,则表示无限等待;如果设置为 0,则表示非阻塞模式;其他值则表示等待的时间(毫秒)。返回值成功时,返回就绪事件文件描述符的数量,最大值为 maxevents。失败时,返回 -1,并设置 errno 以指示错误类型。*/for (int i = 0; i < nfds; i++) {if (events[i].events & EPOLLIN) {// 文件描述符可读,处理读操作}
}

对文件描述符就绪的理解
epoll 的实现中,红黑树确实在管理文件描述符的过程中扮演了重要角色,但就绪状态的判断并不仅仅是“红黑树上的文件描述符状态发生变化”。

(1)红黑树与就绪状态的关系

  • 文件描述符管理epoll 使用红黑树来保存那些被监控的文件描述符。每当你通过 epoll_ctl() 注册或删除一个文件描述符时,红黑树会被更新。这使得 epoll 能高效地增加或删除文件描述符,而不需要像传统的 select 那样每次都遍历整个文件描述符集。

    红黑树的作用是快速地进行插入、删除和查询,以管理哪些文件描述符正在被监控。但红黑树本身并不决定文件描述符是否就绪。

(2)就绪状态的判断

  • 就绪状态的发生:当一个文件描述符(比如 socket)有 I/O 事件发生时(例如网络连接中有数据可读),内核会将其标记为就绪。内核通过检查设备的状态,或者从硬件中断中获得文件描述符的变化情况。当某个事件满足条件后,内核会将这个事件记录下来并触发 epoll 返回。

  • 就绪事件的通知:当事件发生时,epoll 会从红黑树中找出相关的文件描述符,通知用户这些文件描述符可以进行 I/O 操作了。此时这些就绪的文件描述符会被放入一个链表(或者叫作就绪队列)中。调用 epoll_wait() 时,epoll 实际上会返回这个链表中的文件描述符。

  • 文件描述符的状态更新:当 epoll_wait() 返回时,它通知哪些文件描述符有可处理的事件(例如可读、可写)。这并不意味着红黑树上的结构变化了,而是文件描述符的 I/O 状态发生了变化,导致它们被内核认为是“就绪”的。

重要结论

  • 红黑树:主要用于高效管理和存储 epoll 正在监视的文件描述符。
  • 就绪状态:是通过内核监控底层文件描述符的 I/O 状态变化得知的。红黑树本身并不直接用于判断文件描述符是否就绪,只是提供快速的增删查找操作。

epoll 处理流程总结

  1. 文件描述符的注册与管理:使用红黑树管理和查询被监控的文件描述符。
  2. 事件的监听:通过内核事件驱动机制判断哪些文件描述符有 I/O 事件发生。
  3. 就绪队列:当事件发生时,将就绪的文件描述符从红黑树中标记出来,并放入就绪队列,等待用户调用 epoll_wait() 取出处理。

因此,就绪状态的判断是在内核层面完成的,红黑树更多的是为了提供高效的文件描述符管理。

4、为什么不用select

select 是一种经典的 I/O 多路复用机制,但与现代的 epoll 相比,它有一些显著的劣势,因此在性能要求较高的场景下,epoll 被广泛采用。以下是主要原因:

1. 性能问题(O(n) vs. O(1))

  • select:每次调用 select 时,都需要将所有监控的文件描述符集合传递给内核,并检查每一个文件描述符的状态。这意味着 select 的时间复杂度是 O(n),即每次都需要线性扫描整个文件描述符集合。如果监控的文件描述符数量很大,性能会明显下降。

  • epollepoll 使用事件通知机制,而不是遍历文件描述符。文件描述符通过红黑树存储,事件就绪的文件描述符被放入一个就绪队列,调用 epoll_wait() 时直接返回就绪的文件描述符。时间复杂度是 O(1),即只需要处理就绪的文件描述符,避免了不必要的遍历。

2. 文件描述符限制

  • select:有最大文件描述符限制,通常是 1024。这意味着在需要监控大量文件描述符时,select 会遇到系统硬限制,不能处理太多的连接。

  • epoll:没有文件描述符数量的限制,能够轻松处理成千上万个文件描述符的高并发场景。因此,epoll 更适合处理大规模的并发连接。

3. 重复创建文件描述符集合

  • select:每次调用 select 都需要重新构造和传递文件描述符集合,并且在返回时,select 会对该集合进行修改。因此,通常需要在每次调用之前重置文件描述符集合,增加了编程复杂度和性能开销。

  • epoll:一旦将文件描述符注册到 epoll 上,它们会保存在内核的数据结构中,只有状态发生变化时才会被通知。无需在每次调用之前重复创建和传递文件描述符集合。

4. 事件触发机制

  • select:使用水平触发机制(Level-triggered),即如果文件描述符处于就绪状态,select 会一直通知,直到用户处理该事件为止。这可能导致某些场景下出现“假就绪”。

  • epoll:支持边缘触发(Edge-triggered)和水平触发。边缘触发只会在文件描述符从非就绪状态变为就绪状态时通知,有效减少了重复通知,提高了效率。

5. 扩展性

  • select:在需要处理大量文件描述符的高并发服务器上,select 的扩展性较差,性能会随着文件描述符的增加迅速下降。

  • epollepoll 在设计上专为处理大量并发连接而优化,可以轻松支持数以万计的文件描述符,极大提高了扩展性。

总结

在高并发、高性能要求的场景下,select 的 O(n) 复杂度、文件描述符限制以及每次调用时的开销使其性能不足。相比之下,epoll 提供了 O(1) 的性能、灵活的事件通知机制以及更好的扩展性,因此在现代 Linux 系统中,epoll 已经成为处理大量并发连接的标准工具。

5、内核态和用户态的区别?

内核态和用户态是操作系统中两种不同的执行模式,主要用于保护系统资源和实现安全性。

  1. 内核态

    • 运行在操作系统核心的部分,可以直接访问硬件和系统资源。
    • 具有更高的权限,可以执行任何指令,包括访问内存的所有区域。
    • 主要负责管理系统资源(如进程、内存、文件系统等)。
    • 发生错误或崩溃时可能会导致整个系统的崩溃。
  2. 用户态

    • 用户程序运行的模式,权限相对较低。
    • 不能直接访问硬件或操作系统核心的资源,必须通过系统调用与内核进行交互。
    • 保护系统不受用户程序错误的影响,避免用户程序之间的相互干扰。
    • 崩溃时一般只会影响单个程序,不会影响整个系统。

通过这两种模式的区分,操作系统能够在保持系统稳定和安全的同时,允许多个用户程序并发运行。

6、线程的上下文切换问题

线程的上下文切换是指操作系统在运行多个线程时,为了让它们能够共享 CPU 资源而进行的状态保存和恢复的过程。上下文切换的主要步骤和特点包括:

上下文切换的步骤:

  1. 保存当前线程状态

    • 将当前运行线程的寄存器状态、程序计数器(PC)、堆栈指针等信息保存到线程控制块(TCB)中。
  2. 选择下一个线程

    • 根据调度算法选择下一个要运行的线程。
  3. 加载新线程状态

    • 从选定的线程的 TCB 中恢复其寄存器状态、程序计数器和堆栈指针。
  4. 切换到新线程

    • 让 CPU 开始执行新线程的代码。

特点:

  • 性能开销

    • 上下文切换会产生性能开销,包括保存和恢复状态的时间,以及 CPU Cache 的失效。因此,频繁的上下文切换会影响系统的整体性能。
  • 多线程模型

    • 在多线程应用中,合理的调度和减少上下文切换是优化性能的关键。
  • 内核态与用户态

    • 上下文切换可以发生在用户态线程之间,也可以在内核态线程(如系统线程)与用户态线程之间进行,后者通常开销更大。
  • 线程库

    • 用户态线程库(如 pthreads)可能使用用户级的上下文切换,而内核级线程则依赖操作系统的调度。

通过合理管理线程的上下文切换,可以提高程序的并发性能和响应速度。

7、就绪状态和阻塞状态有什么区别 两者可以互相切换吗

就绪状态和阻塞状态是操作系统中线程或进程的两种基本状态,它们的主要区别如下:

就绪状态:

  • 定义:就绪状态表示线程或进程已准备好运行,但由于 CPU 资源不可用而无法执行。它在就绪队列中等待 CPU 的调度。
  • 特点
    • 随时可以被调度执行。
    • 处于就绪状态的线程可以在适当时机(如 CPU 可用时)迅速转入运行状态。

阻塞状态:

  • 定义:阻塞状态表示线程或进程因等待某些事件(如 I/O 操作完成、信号量或条件变量)而无法继续执行。它处于阻塞队列中,等待特定事件的发生。
  • 特点
    • 不能被调度执行,直到等待的事件发生。
    • 例如,线程在进行文件读取时可能会进入阻塞状态,直到数据读取完成。

状态之间的切换:

  • 可以互相切换
    • 就绪状态到运行状态:当 CPU 可用时,操作系统调度一个就绪状态的线程进入运行状态。
    • 阻塞状态到就绪状态:当阻塞的事件发生时(如 I/O 完成),阻塞状态的线程可以转变为就绪状态,等待 CPU 调度。

这种状态管理机制有助于实现高效的资源利用和提高系统的并发性能。

8、多线程的优缺点 多核cpu多线程 线程切换主要消耗的是什么资源

多线程的优缺点

优点:
  1. 并发性:多个线程可以并行执行,提高程序的响应速度和整体性能,尤其在 I/O 密集型和计算密集型任务中。
  2. 资源共享:线程可以共享进程的资源(如内存),减少了内存开销。
  3. 提高资源利用率:在多核 CPU 上,多线程可以更好地利用所有 CPU 核心,提高系统的处理能力。
  4. 响应性:在用户界面应用中,多线程可以使 UI 线程保持响应,同时处理后台任务。
缺点:
  1. 复杂性:多线程编程增加了程序设计和调试的复杂性,可能导致竞争条件和死锁等问题。
  2. 上下文切换开销:频繁的线程切换会导致性能下降,尤其是当上下文切换频繁时。
  3. 共享资源管理:需要小心管理共享资源,以避免数据不一致和线程安全问题。
  4. 调试困难:多线程程序的错误可能难以重现和调试,尤其是涉及到异步操作时。

多核 CPU 上的多线程

在多核 CPU 上,多线程能够显著提高性能,因为多个线程可以在不同的核心上并行执行。这种并行性能够更充分地利用 CPU 的计算能力,减少等待时间,特别是在计算密集型任务中。

线程切换主要消耗的资源

  1. CPU 时间:保存和恢复线程状态(如寄存器和程序计数器)的过程会消耗 CPU 时间。
  2. 内存:线程切换涉及保存线程的上下文信息到线程控制块(TCB),需要占用一定的内存资源。
  3. 缓存失效:线程切换可能导致 CPU 缓存中的数据失效,从而需要重新加载数据,影响性能。
  4. 调度延迟:在多线程环境中,调度算法的开销也会影响线程切换的效率。

通过合理设计和管理多线程程序,可以在发挥其优点的同时,尽量减少缺点带来的影响。

9、线程和进程的状态都是那5个

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 视觉距离与轴距离的转换方法
  • C++标准库容器类——string类
  • 基于 K8S kubernetes 搭建 安装 EFK日志收集平台
  • Mysql InnoDB 存储引擎简介
  • 速盾:高防服务器租用需要注意什么事项
  • Mac 上,终端如何开启 proxy
  • vulnhub-prime1
  • YARN性能优化高频面试题及答案
  • 大表数据如何在OceanBase中进行表分区管理的实践
  • Shiro-550—漏洞分析(CVE-2016-4437)
  • 【笔记】第二节 轧制、热处理和焊接工艺
  • 同一时刻最多有多少会议进行
  • EP25 调试接口渲染分类页面
  • Android 内核开发之—— repo 使用教程
  • 电线覆盖物检测数据集 气球风筝鸟巢 1300张 voc yol
  • 【跃迁之路】【669天】程序员高效学习方法论探索系列(实验阶段426-2018.12.13)...
  • 11111111
  • Android路由框架AnnoRouter:使用Java接口来定义路由跳转
  • Angularjs之国际化
  • docker python 配置
  • Hibernate【inverse和cascade属性】知识要点
  • JavaScript 事件——“事件类型”中“HTML5事件”的注意要点
  • Just for fun——迅速写完快速排序
  • Linux编程学习笔记 | Linux IO学习[1] - 文件IO
  • node学习系列之简单文件上传
  • python3 使用 asyncio 代替线程
  • RedisSerializer之JdkSerializationRedisSerializer分析
  • 从tcpdump抓包看TCP/IP协议
  • 漫谈开发设计中的一些“原则”及“设计哲学”
  • 排序算法学习笔记
  • 浅谈web中前端模板引擎的使用
  • 如何编写一个可升级的智能合约
  • 如何打造100亿SDK累计覆盖量的大数据系统
  • 验证码识别技术——15分钟带你突破各种复杂不定长验证码
  • Nginx实现动静分离
  • # Apache SeaTunnel 究竟是什么?
  • #、%和$符号在OGNL表达式中经常出现
  • #14vue3生成表单并跳转到外部地址的方式
  • #单片机(TB6600驱动42步进电机)
  • $.proxy和$.extend
  • (06)金属布线——为半导体注入生命的连接
  • (1)常见O(n^2)排序算法解析
  • (20050108)又读《平凡的世界》
  • (4)事件处理——(7)简单事件(Simple events)
  • (el-Date-Picker)操作(不使用 ts):Element-plus 中 DatePicker 组件的使用及输出想要日期格式需求的解决过程
  • (pytorch进阶之路)CLIP模型 实现图像多模态检索任务
  • (ZT)薛涌:谈贫说富
  • (翻译)terry crowley: 写给程序员
  • (附源码)springboot家庭装修管理系统 毕业设计 613205
  • (免费领源码)Java#Springboot#mysql农产品销售管理系统47627-计算机毕业设计项目选题推荐
  • (十二)devops持续集成开发——jenkins的全局工具配置之sonar qube环境安装及配置
  • (四十一)大数据实战——spark的yarn模式生产环境部署
  • (一)u-boot-nand.bin的下载
  • (转)程序员技术练级攻略
  • (转)关于多人操作数据的处理策略