服务优雅下线

服务优雅下线

问题背景描述

  • 运维同事反馈上线滚动发布,旧实例接收的请求发生业务中断,没有处理完成, 为保证服务质量,期望发布时旧实例将已有请求处理完成再回收。
  • 先说可能会带来的影响:
  • 业务处理中断
    • 导致数据不完整,属于致命危害。
    • 举例:文件写入中断,文件数据不完整;支付完成,本地业务处理失败。
    • 应用是可以通过一些手段保证业务流程,比如业务补偿、幂等设计、状态机严格控制,这要求编程时考虑因素更大,即使能做到这些,也会带来维护成本,出现问题需要排查,甚至人工补偿,保证数据完整性。
  • 已下线的服务继续提供服务
    • 微服务一般都由注册中心提供的服务自动发现机制,在一定时间范围内,即使服务下线,注册中心和服务有心跳时间窗口,比如eureka默认是30S,这个时间窗口内,注册中心不会下线服务,导致上游调用该服务大量报错,引发告警甚至熔断,业务请求大量失败。
    • 服务不存在绝对的优雅下线,一方面有断电、拔电源等外在因素;还有kill -15后应用本身不能在正常下线窗口时间范围内处理完成业务,触发 kill -9,发生业务中断。
      但我们可以在可以控制的范围内,做到优雅下线。
    • 在这里插入图片描述

优雅下线要解决的问题

处理完已经接受的请求

  • 正常执行完成后台业务逻辑
  • 调用方接收正常处理完成的信号

不再接收新的请求

  • 目前部署架构和发布方式,发布采用滚动发布的方式,k8s已经实现了停机时将服务从注册列表自动移除的机制。

  • k8s对pod是有一个terminationGracePeriodSeconds的配置项(默认是30秒),当采用RollingUpdate方式更新应用时,一个新pod起来并经过healthcheck后,会通知k8s终止一个pod。Pod被设置为Terminating状态,并从k8s的service/endpoint列表中删除,Pod停止获得新流量。K8S向Pod中的容器发送SIGTERM(kill 15)信号,通知应用进程。

  • 这给应用层面省去了不少麻烦,不然得下线时挂钩子(shutdownHook)将服务主动从注册列表移除,还没那么简单,容器重启服务时,如果发布前后ip无变化,还得对外暴露接口,配置执行脚本将服务加入注册列表,需要一整套方案去保证。

  • DiscoveryManager.getInstance().shutdownComponent();
    
  • 测试代码:

  • @GetMapping("/test")public String test1(@RequestParam int s) throws InterruptedException {System.out.println(Thread.currentThread().getName() + " start");TimeUnit.SECONDS.sleep(s);System.out.println(Thread.currentThread().getName() + " finished");return "m1";}
  • Jetty 停机日志:可以看到系统会处理完成请求,但是客户端接收不到响应!

  • qtp1648582256-24 start
    2022-04-18 17:21:54.040  INFO 28284 --- [      Thread-13] ConfigServletWebServerApplicationContext : Closing org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@647e447: startup date [Mon Apr 18 17:21:38 CST 2022]; root of context hierarchy
    2022-04-18 17:21:54.041  INFO 28284 --- [      Thread-13] o.s.j.e.a.AnnotationMBeanExporter        : Unregistering JMX-exposed beans on shutdown
    2022-04-18 17:21:54.050  INFO 28284 --- [      Thread-13] o.e.jetty.server.AbstractConnector       : Stopped ServerConnector@2caf6912{HTTP/1.1,[http/1.1]}{0.0.0.0:8080}
    2022-04-18 17:21:54.050  INFO 28284 --- [      Thread-13] org.eclipse.jetty.server.session         : Stopped scavenging
    2022-04-18 17:21:54.053  INFO 28284 --- [      Thread-13] o.e.j.s.h.ContextHandler.application     : Destroying Spring FrameworkServlet 'dispatcherServlet'
    2022-04-18 17:21:54.054  INFO 28284 --- [      Thread-13] o.e.jetty.server.handler.ContextHandler  : Stopped o.s.b.w.e.j.JettyEmbeddedWebAppContext@5910de75{/,[file:///private/var/folders/fd/ts66c75s6pl47q8cy2q5szy80000gn/T/jetty-docbase.7513543250728756019.8080/],UNAVAILABLE}
    qtp1648582256-24 finishedProcess finished with exit code 130 (interrupted by signal 2: SIGINT)
  • jetty客户端响应

  • org.apache.http.NoHttpResponseException: localhost:8080 failed to respond
    
  • tomcat 停机日志:可以看到系统收到指令,业务中断

  • http-nio-8080-exec-2 start
    2022-04-18 17:37:19.863  INFO 28365 --- [       Thread-6] ConfigServletWebServerApplicationContext : Closing org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@6580cfdd: startup date [Mon Apr 18 17:36:33 CST 2022]; root of context hierarchy
    2022-04-18 17:37:19.865  INFO 28365 --- [       Thread-6] o.s.j.e.a.AnnotationMBeanExporter        : Unregistering JMX-exposed beans on shutdownProcess finished with exit code 130 (interrupted by signal 2: SIGINT)
  • 客户端响应

  • org.apache.http.NoHttpResponseException: localhost:8080 failed to respond。
    

Springboot对优雅关闭的支持

  • 根据关闭打印日志可以看到,关闭相关类AnnotationConfigEmbeddedWebApplicationContext,
    父类AbstractApplicationContext提供了对优雅下线的扩展支持,Springboot 在关闭时,如果有请求没有响应完,
    在不同的容器会出现不同的结果,实际上具体关闭处理是交给具体容器自己实现的。

  • 贴一下关键源码:

  • /*** Register a shutdown hook with the JVM runtime, closing this context* on JVM shutdown unless it has already been closed at that time.* <p>Delegates to {@code doClose()} for the actual closing procedure.* @see Runtime#addShutdownHook* @see #close()* @see #doClose()*/@Overridepublic void registerShutdownHook() {if (this.shutdownHook == null) {// No shutdown hook registered yet.this.shutdownHook = new Thread() {@Overridepublic void run() {synchronized (startupShutdownMonitor) {doClose();}}};Runtime.getRuntime().addShutdownHook(this.shutdownHook);}}/*** Actually performs context closing: publishes a ContextClosedEvent and* destroys the singletons in the bean factory of this application context.* <p>Called by both {@code close()} and a JVM shutdown hook, if any.* @see org.springframework.context.event.ContextClosedEvent* @see #destroyBeans()* @see #close()* @see #registerShutdownHook()*/protected void doClose() {if (this.active.get() && this.closed.compareAndSet(false, true)) {if (logger.isInfoEnabled()) {logger.info("Closing " + this);}LiveBeansView.unregisterApplicationContext(this);try {// Publish shutdown event.publishEvent(new ContextClosedEvent(this));}catch (Throwable ex) {logger.warn("Exception thrown from ApplicationListener handling ContextClosedEvent", ex);}// Stop all Lifecycle beans, to avoid delays during individual destruction.try {getLifecycleProcessor().onClose();}catch (Throwable ex) {logger.warn("Exception thrown from LifecycleProcessor on context close", ex);}// Destroy all cached singletons in the context's BeanFactory.destroyBeans();// Close the state of this context itself.closeBeanFactory();// Let subclasses do some final clean-up if they wish...onClose();this.active.set(false);}}

为什么tomcat默认不会优雅关闭?

  • tomcat 未对服务关闭事件做特殊处理,业务处理中断,调用方报错。

jetty的优雅关闭有什么问题?

  • 接收到的请求能继续处理,调用方会报错,原因是Jetty中Server.stop()方法先关闭connections,然后处理队列中未完成的request,request完成后,由于connection已经关闭了,响应不能写回去了。

Springboot内嵌容器优雅停机的处理方案

Spring Boot 2.3 及以上版本,开箱即用
  • ## 开启优雅停机, 如果不配置是默认IMMEDIATE, 立即停机
    server.shutdown=graceful
    ## 优雅停机宽限期时间
    spring.lifecycle.timeout-per-shutdown-phase=30s
Spring Boot 2.3 以下版本
  • 相当于手动实现高版本功能,完美一点,可以做开关和关闭时间可配置化。

  • (1) Jetty 实现:

  • 先看一下jetty核心类org.eclipse.jetty.server.Server两个关键的方法

  • /*** Set a graceful stop time.* The {@link StatisticsHandler} must be configured so that open connections can* be tracked for a graceful shutdown.* @see org.eclipse.jetty.util.component.ContainerLifeCycle#setStopTimeout(long)*/
    @Override
    public void setStopTimeout(long stopTimeout)
    {super.setStopTimeout(stopTimeout);
    }/** Set stop server at shutdown behaviour.* @param stop If true, this server instance will be explicitly stopped when the* JVM is shutdown. Otherwise the JVM is stopped with the server running.* @see Runtime#addShutdownHook(Thread)* @see ShutdownThread*/
    public void setStopAtShutdown(boolean stop)
    
  • 解决方法:Jetty 是用 StatisticsHandler 做请求数量监控,关闭应用时,请求数量从N变为O的过程其实就是优雅关闭。

  • @Bean
    public EmbeddedServletContainerFactory jettyEmbeddedServletContainerFactory() {JettyEmbeddedServletContainerFactory factory = new JettyEmbeddedServletContainerFactory();factory.addServerCustomizers(server -> {server.setStopAtShutdown(false);StatisticsHandler statisticsHandler = new StatisticsHandler();statisticsHandler.setHandler(server.getHandler());server.setHandler(statisticsHandler);server.setStopTimeout(30000); // 为了简单,写死30s});return factory;
    }
    
  • (2) Tomcat 实现:

  •   @Beanpublic EmbeddedServletContainerCustomizer tomcatCustomizer() {return container -> {if (container instanceof TomcatEmbeddedServletContainerFactory) {((TomcatEmbeddedServletContainerFactory) container).addConnectorCustomizers(new GracefulShutdown());}};}private static class GracefulShutdown implements TomcatConnectorCustomizer,ApplicationListener<ContextClosedEvent> {private static final Logger log = LoggerFactory.getLogger(GracefulShutdown.class);private volatile Connector connector;@Overridepublic void customize(Connector connector) {this.connector = connector;}@Overridepublic void onApplicationEvent(ContextClosedEvent event) {this.connector.pause();Executor executor = this.connector.getProtocolHandler().getExecutor();if (executor instanceof ThreadPoolExecutor) {try {ThreadPoolExecutor threadPoolExecutor = (ThreadPoolExecutor) executor;threadPoolExecutor.shutdown();if (!threadPoolExecutor.awaitTermination(30, TimeUnit.SECONDS)) {log.warn("Force shutdown ...");}} catch (InterruptedException ex) {Thread.currentThread().interrupt();}}}}
    

开发需要注意的事项

  • 评估每个服务停机处理完请求需要的下线时间

  • 保证应用在下线指定时间内的请求都能正常处理并且响应调用方

  • 手动对自定义线程资源优雅下线

  • 业务处理线程资源释放示例:

  • @Bean
    public ExecutorService bizExecutorService() {ExecutorService executorService = Executors.newFixedThreadPool(10);// shutdownAndAwaitTermination  可以参考guava线程池优雅关闭Runtime.getRuntime().addShutdownHook(new Thread(() -> shutdownAndAwaitTermination(executorService, 10l, TimeUnit.SECONDS)));return executorService;
    }
    
  • 对生产发布因未在指定时间内完成的业务请求进行监控,并优化性能

Spring Boot graceful shutdown

  • Graceful shutdown:

  • Graceful shutdown is supported with all four embedded web servers (Jetty, Reactor Netty, Tomcat, and Undertow) and with both reactive and Servlet-based web applications. 
    When enabled using server.shutdown=graceful, upon shutdown, the web server will no longer permit new requests and will wait for a grace period for active requests to complete. 
    The grace period can be configured using spring.lifecycle.timeout-per-shutdown-phase. Please see the reference documentation for further details.
    
  • Spring Boot 2.3.0.RELEASE引入了Graceful Shutdown的功能。其中应用在等待下线期间对待新请求的方式,取决于我们所使用的 Server 类型。根据官方文档Tomcat、Jetty 和 Reactor Netty将会在网络层面停止接收新的请求。Undertow 会继续接收新的请求,但立即会以 HTTP 503(服务不可用)来响应。

配置和使用
  • 在Spring Boot 2.3.0中,优雅停机的使用非常简单,可以通过在应用程序配置文件中设置两个属性来进行。
  • 1、 server.shutdown 属性可以支持的值有两种
    immediate 这是默认值,配置后服务器立即关闭,无优雅停机逻辑。
    graceful 开启优雅停机功能,并遵守 spring.lifecycle.timeout-per-shutdown-phase 属性中给出的超时来作为服务端等待的最大时间。
  • 2、spring.lifecycle.timeout-per-shutdown-phase 服务端等待最大超时时间,采用java.time.Duration格式的值,默认30s。
  • 当我们使用了如上配置开启了优雅停机功能,当我们通过SIGTERM信号关闭 Spring Boot 应用时
  • 1、 此时如果应用中没有正在进行的请求,应用程序将会直接关闭,而无需等待超时时间结束后才关闭。
  • 2、此时如果应用中有正在处理的请求,则应用程序将等待超时时间结束后才会关闭。如果应用在超时时间之后仍然有未处理完的请求,应用程序将抛出异常并继续强制关闭。
graceful shutdown源码实现分析
  • 我们以 Tomcat 为例看一下是SpringBoot 2.3如何实现graceful shutdown的来看一下doShutdown的逻辑

  • org.springframework.boot.web.embedded.tomcat.GracefulShutdown#doShutdown
    private void doShutdown(GracefulShutdownCallback callback) {List<Connector> connectors = getConnectors();
    connectors.forEach(this::close);
    try {for (Container host : this.tomcat.getEngine().findChildren()) {for (Container context : host.findChildren()) {while (isActive(context)) {if (this.aborted) {logger.info("Graceful shutdown aborted with one or more requests still active");callback.shutdownComplete(GracefulShutdownResult.REQUESTS_ACTIVE);return;}Thread.sleep(50);}}}}
    catch (InterruptedException ex) {Thread.currentThread().interrupt();
    }
    logger.info("Graceful shutdown complete");
    callback.shutdownComplete(GracefulShutdownResult.IDLE);
    }
    
  • 先是关闭掉所有的连接,在网络层停止接受请求,然后再等待所有请求处理完毕。

  • 在微服务场景下问题似乎依旧存在…

  • 总结一下一个 Spring Cloud 应用正常分批发布的流程

    • 1、服务发布前,消费者根据负载均衡规则调用服务提供者,业务正常。
    • 2、服务提供者 B 需要发布新版本,先对其中的一个节点进行操作,先是正常停止 Java 进程。
    • 3、服务停止过程中,首先去注册中心注销服务,然后等待服务端线程处理完成,再停止服务。
    • 4、注册中心则将通知消费者,其中的一个服务提供者节点已下线。这个过程包含推送和轮询两种方式,推送可以认为是准实时的,轮询的耗时由服务消费者轮询间隔决定,最差的情况下需要 1 分钟。
    • 5、服务消费者刷新服务列表,感知到服务提供者已经下线了一个节点,但是这个过程中Spring Cloud 的负载均衡组件 Ribbon 默认的刷新时间是 30 秒 ,最差情况下需要耗时 30 秒。
    • 6、服务消费者不再调用已经下线的节点
  • 在这里插入图片描述

  • 我们看到,当一个Spring Cloud服务端通过SpringBoot提供的graceful shutdown下线时,它会拒绝客户端新的请求,并且等待已经在处理的线程处理完成后,或者在配置的应用最长等待时间到了之后进行下线。

  • 但是在服务端重启开始拒绝客户端新的请求的时刻开始,即执行了Connectors.stop开始,到客户端感知到服务端该实例下线这段时间内,客户端向该实例发起的所有请求都会被拒绝,从而引起服务调用异常。

  • 在这里插入图片描述

  • 如果客户端考虑增加重试能力,这一定程度上可以缓解发布过程中服务调用报错的问题,但是无法根本上保证下线过程的无损,如果服务调用报错期过程,或者分批发布时候同一批次下线的节点数过多,无法保证仅仅增加多次重试就能够调用到未下线的节点上。这不能根本解决问题!
    同时需要考虑配置重试带来的业务上存在不幂等的风险。

长连接服务的重启

  • 我们很多时候都需要安全的将服务停止,也就是把没有处理完的工作继续处理完成。比如停止一些依赖的服务,输出一些日志,发一些信号给其他的应用系统等,这个在保证系统的高可用中是非常有必要的。
  • 长连接服务,管理了大量的TCP 连接FD,绝不能直接暴力关闭,否则OOM。必须要保证已有的任务全都处理完,并且在关闭的过程中不会有新的请求连接进来。

优雅关闭

  • 服务的优雅关闭通常都是利用 JDK 里面提供的 Runtime.addShutDownHook(Thread hook)方法,JVM 提供一种 ShutdownHook(钩子)机制,当 JVM 接受到系统的关闭通知之后,会调用 ShutdownHook 内的方法,用以完成清理操作,从而平滑的退出应用。
  • 像Spring 、Dubbo 等都是基于这个实现的优雅关闭,而Netty 自身提供了优雅退出的方式,那就是 EventExecutorGroup 的 shutdownGracefully() 方法。

长连接的平滑重启

  • 长连接相关的事情,最大的感触就是发版太痛苦,一个个踢掉连接然后发版,导致发版时长过长,操作繁琐。所以在想能不能实现优雅重启, 发版时客户端无感知。
  • 实现思路:
  • 主要思路就是要解决新老进程切换过程中listener文件描述符和现有连接描述符的迁移,之前有一篇文章讲了如何通过unixSocket来传递文件描述符,这里也会用到这个技术, 具体的迁移流程如下:
    • 1、老进程在启动时,启动tcp listen监听,就是开启对外服务的端口,如果有请求过来,开启一个协程处理这个请求。
    • 2、老进程启动一个unix Socket监听,通过switch.sock和新进程进行通信,一旦由请求过来,就把自己的listener文件描述符发给新进程,然后通过发送信号触发迁移流程,同时等待一段时间关闭自己的listener,进程退出。
    • 3、新进程在启动前,会尝试连接旧进程的switch.sock,如果通信成功,就知道它需要开始接收旧的listener 和 connection了,此时会分别启动listen.sock监听和conn.sock监听,来获取旧进程的listener信息和conn信息。如果收到了listener信息,则会用这个listener启动服务。
    • 4、接步骤1,协程在处理请求的过程中,如果收到了迁移信号,则会判断是否将现有的客户端连接发给新进程,如果连接里还有可读的数据,则需要迁移。
      到此整个迁移流程就完毕了。

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

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

相关文章

开源项目CuteSqlite开发笔记(八):Windows 64位/32位使用GetWindowLongPtr钩子函数

需求描述 在开发CuteSqlite的时候&#xff0c; 有一个功能需要实现&#xff0c;鼠标移到WTL::CStatic上后&#xff0c;发送消息通知CToolTipCtrl弹出。 遇到问题 WTL::CStatic控件没有相应 WM_MOUSEMOVE 消息&#xff0c;需要返回一个HTCLIENT消息来让窗口处理函数执行 WM_MO…

【QT+QGIS跨平台编译】之六:【LZMA+Qt跨平台编译】(一套代码、一套框架,跨平台编译)

文章目录 一、lzma介绍二、文件下载三、文件分析四、pro文件五、编译实践一、lzma介绍 LZMA(Lempel-Ziv-Markov chain-Algorithm的缩写),是一个Deflate和LZ77算法改良和优化后的压缩算法。 libLzma是基于LZMA压缩算法封装的开源库。2001年被首次应用于7-Zip压缩工具中,是 …

点亮流水灯

目录 1.water_led 2.tb_water_led 50MHZ一个周期是20ns,0.5秒就是20ns0.02um0.00002ms0.000_00002s。0.5/0.000_00002s25_000_000个时钟周期&#xff0c;表示要从0计数到24_999_999 LED灯是低电平点亮&#xff0c;前0.5秒点亮第一个LED灯&#xff0c;当检测到脉冲信号点亮第二…

关于缓存 db redis local 取舍之道

文章目录 前言一、影响因素二、db or redis or local1.db2.redis3. local 三、redisson 和 CaffeineCache 封装3.1 redisson3.1.1 maven3.1.2 封装3.1.3 使用 3.2 CaffeineCache3.1.1 maven3.1.2 封装3.1.3 使用 总结 前言 让我们来聊一下数据缓存&#xff0c;它是如何为我们带…

【C/C++】C/C++编程——为什么学习 C++?

当提到C的时候&#xff0c;很多人会觉得语法复杂、学习曲线陡峭&#xff0c;并且好像与C语言还有点"纠缠不清"。尽管如此&#xff0c;C仍然是当今世界上最受欢迎和最有影响力的编程语言之一。特别是在当今快速发展的人工智能&#xff08;AI&#xff09;领域&#xff…

vue3前端开发,如何引入element-plus前端框架及配置参数

vue3前端开发,如何引入element-plus前端框架及配置参数&#xff01;这是一个简单的教程&#xff0c;帮助大家快速在自己的项目中引入element-plus框架。 主要是介绍的引入流程和参数的配置情况。 如图&#xff0c;这个就是elment-plus前端框架里面的一个主按钮展示。表示我们配…

NodeJs 第二十章 代理

在计算机网络中&#xff0c;代理是一种中间服务&#xff0c;能够代理用户与网络资源之间的通信。代理服务器可以缓存网页内容、过滤网络流量或隐藏用户的真实IP地址等功能。 在日常开发中&#xff0c;我们接触最多的是客户端发送ajax到服务端。但是服务端并不是 node &#xf…

pod 报错Failed to connect to github.com port 443

pod 报错Failed to connect to github.com port 443 1、排查代理问题1.1、查找网络代理1.2、修改 Git 的代理 2、排查DNS解析问题2.1、查找 ip地址2.2、修改 host 文件 1、排查代理问题 1.1、查找网络代理 打开 设置 --> 网络与Internet --> 查找代理 1.2、修改 Git …

k8s中服务器容器tcp连接数量优化

netty的http1服务器在运行一段时间后会无法提供服务&#xff0c;返回客户端socket hang up 使用apipost测试抓包显示三次握手后被reset 修改net/core/somaxconn 登录容器&#xff0c;cat /proc/sys/net/core/somaxconn显示128&#xff0c;对于一个服务器来说&#xff0c;这个…

PLAN方法:解决 GAN 生成医学图像 Latent 空间中的隐私保护方法

PLAN方法&#xff1a;解决 GAN 生成医学图像 Latent 空间中的隐私保护方法 PLAN 原理StyleGAN 生成视网膜图k-SALSA 生成视网膜图PLAN方法 生成视网膜图 总结 PLAN 原理 论文&#xff1a;https://arxiv.org/abs/2307.02984 代码&#xff1a;https://github.com/perceivelab/P…

vulhub之redis篇

CVE-2022-0543 | redis的远程代码执行漏洞 简介 CVE-2022-0543 该 Redis 沙盒逃逸漏洞影响 Debian 系的 Linux 发行版本,并非 Redis 本身漏洞, 漏洞形成原因在于系统补丁加载了一些redis源码注释了的代码 原理分析 redis一直有一个攻击面,就是在用户连接redis后,可以通过ev…

企业微信开发:本地运行一个页面应用

问题 在开发环境本地运行一个页面应用&#xff0c;将网页URL配置到企业微信的应用主页网址中&#xff0c;此时应用在企业微信中能够正常打开网页吗&#xff1f; 结论是&#xff1a;能够正常访问页面。 能够访问的前提 能够访问的前提条件&#xff0c;企业微信客户端所在的网…

界面控件DevExpress ASP.NET Data Grid组件 - 可快速处理各类型数据!(一)

由DevExpress开发的快速且功能完整的ASP.NET Web Forms的Data Grid组件&#xff0c;从全面的数据塑造和数据过滤选项到十多个集成数据编辑器&#xff0c;该套件提供了帮助用户构建极佳数据所需的一些&#xff0c;没有限制&#xff01; P.S&#xff1a;DevExpress ASP.NET Web …

k8s--helm

什么是helm&#xff1f;在没有这个helm之前&#xff0c;deployment service ingress helm的作用 通过打包的方式&#xff0c;把deployment service ingress等打包在一块&#xff0c;一键式的部署服务&#xff0c;类似yum安装 官方提供的一个类似与安装仓库额功能&#xff0c;…

C语言——结构体讲解

目录 一、结构体类型的声明 二、结构体变量的定义和初始化 三、结构体的重命名 四、结构体的自引用 五、结构体内存对齐 六、结构体传参 七、结构体实现位段 7.1 什么是位段 7.2 位段的声明和使用 7.3 位段的空间大小计算 7.4 位段的内存分配 7.5 位段的跨平…

计算机网络-物理层基本概念(接口特性 相关概念)

文章目录 总览物理层接口特性星火模型给出的相关概念解释&#xff08;仅供参考&#xff09; 总览 求极限传输速率&#xff1a;奈氏准则&#xff0c;香农定理&#xff08;背景环境不一样&#xff09; 编码&#xff1a;数据变成数字信号 调制&#xff1a;数字信号变成模拟信号 信…

Elasticsearch:2023 年 Lucene 领域发生了什么?

作者&#xff1a;来自 Elastic Adrien Grand 2023 年刚刚结束&#xff0c;又是 Apache Lucene 开发活跃的一年。 让我们花点时间回顾一下去年的亮点。 社区 2023 年&#xff0c;有&#xff1a; 5 个次要版本&#xff08;9.5、9.6、9.7、9.8 和 9.9&#xff09;&#xff0c;1 …

51单片机LED点阵屏

LED点阵屏 LED点阵屏是一种由许多小型LED灯组成的矩阵式显示屏。这些LED灯可以是单色、双色或全彩的&#xff0c;它们排列成行和列的网格&#xff0c;可以根据需要点亮来显示图像、文字或动画等内容。LED点阵屏广泛应用于户外广告牌、室内显示、交通信号灯、电子价格标签和其他…

【设计模式】字节三面:请举例阐释访问者模式

今天我们要一起探讨的主题是一种设计模式——访问者模式(Visitor Pattern)。我将从最基础的概念、应用场景&#xff0c;再到实例代码的展示&#xff0c;全方位的为大家剖析访问者模式。而且&#xff0c;我保证&#xff0c;你即使是编程新手&#xff0c;也能理解并开始应用这个设…

如何在 Ubuntu 22.04 上安装 Linux、Apache、MySQL、PHP (LAMP) 堆栈

前些天发现了一个人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;最重要的屌图甚多&#xff0c;忍不住分享一下给大家。点击跳转到网站。 如何在 Ubuntu 22.04 上安装 Linux、Apache、MySQL、PHP (LAMP) 堆栈 介绍 “LAMP”堆栈是一组开源软件&#…