微服务优雅上下线的实践方法

news2024/7/4 6:04:14

导语

本文介绍了微服务优雅上下线的实践方法及原理,包括适用于 Spring 应用的优雅上下线逻辑和服务预热,以及使用 Docker 实现无损下线的 Demo。同时,本文还总结了优雅上下线的价值和挑战。

作者简介

颜松柏

腾讯云微服务架构师

拥有超过10年的 IT 从业经验,精通软件架构设计,微服务架构,云架构设计等多个领域,在泛互、金融、教育、出行等多个行业拥有丰富的微服务架构经验。

前言

图片

微服务优雅上下线的原理是指在微服务的发布过程中,保证服务的稳定性和可用性,避免因为服务的变更而造成流量的中断或错误。

微服务优雅上下线的原理可以从三个角度来考虑:

  • 服务端的优雅上线,即在服务启动后,等待服务完全就绪后再对外提供服务,或者有一个服务预热的过程。

  • 服务端的无损下线,即在服务停止前,先从注册中心注销,拒绝新的请求,等待旧的请求处理完毕后再下线服务。

  • 客户端的容灾策略,即在调用服务时,通过负载均衡、重试、黑名单等机制,选择健康的服务实例,避免调用不可用的服务实例。

微服务优雅上下线可以提高微服务的稳定性和可靠性,减少发布过程中的风险和损失。

优雅上线

[外链图片转存中…(img-rvW8Rj8q-1689158362085)]

优雅上线,也叫无损上线,或者延迟发布,或者延迟暴露,或者服务预热。

优雅上线的目的是为了提高发布的稳定性和可靠性,避免因为应用的变更而造成流量的中断或错误。

优雅上线的方法

优雅上线的方法有以下几种:

  • 延迟发布:即延迟暴露应用服务,比如应用需要一些初始化操作后才能对外提供服务,如初始化缓存,数据库连接池等相关资源就位,可以通过配置或代码来实现延迟暴露。

  • QoS 命令:即通过命令行或 HTTP 请求来控制应用服务的上线和下线,比如在应用启动时不向注册中心注册服务,而是在服务健康检查完之后再手动注册服务。

  • 服务注册与发现:即通过注册中心来管理应用服务的状态和路由信息,比如在应用启动时向注册中心注册服务,并监听服务状态变化事件,在应用停止时向注册中心注销服务,并通知其他服务更新路由信息。

  • 灰度发布:即通过分流策略来控制应用服务的流量分配,比如在发布新版本的应用时,先将部分流量导入到新版本的应用上,观察其运行情况,如果没有问题再逐步增加流量比例,直到全部切换到新版本的应用上。

上面的方法核心思想都是一个,就是等服务做好了准备再把请求放行过去。

优雅上线的实现

大部分优雅上线都是通过注册中心和服务治理能力来实现的。

对于初始化过流程较长的应用,由于注册通常与应用初始化过程同步进行,因此可能出现应用还未完全初始化就已经被注册到注册中心供外部消费者调用,此时直接调用可能会导致请求报错。

所以,通过服务注册与发现来做优雅上线的基本思路是:

  • 在应用启动时,提供一个健康检查接口,用于反馈服务的状态和可用性。

  • 应用启动后,可以采用下列方法来使新的请求暂时不进入新版的服务实例。

    • 暂时不向注册中心注册服务。
    • 隔离服务,有些注册中心支持隔离服务实例,比如北极星。
    • 将权重配置为0。
    • 将服务实例的 Enable 改为 False。
    • 让健康检查接口返回不健康的状态。
  • 在新版本的应用实例完成初始化操作后,确保了可用性后,再对应的将上述的方法取消,这样就可以让新的请求被路由到新版本的应用实例上。

  • 如果需要预热,就让流量进入新版本的应用实例时按比例的一点点增加。

这样,就可以实现优雅上线的过程,保证请求进来的时候,不会因为新版本的应用实例没有准备好而导致请求失败。

优雅上线的北极星代码 Demo

我们以 Spring Cloud 和 北极星 为例,讲一下如何通过服务注册与发现来做优雅上线的过程。

首先,我们需要创建一个 Spring Cloud 项目,并添加北极星的依赖。

然后,我们需要在 application.properties 文件中配置北极星的相关信息,如注册中心地址,服务名,分组名等,例如:

spring:

application:

name: ${application.name}

cloud:

polaris:

address: grpc://${修改为第一步部署的 Polaris 服务地址}:8091

namespace: default

然后,我们需要创建一个 Controller 类,提供一个简单的接口,用于返回服务的信息,例如:

@RestController

public class ProviderController {

@Value("${server.port}")

private int port;

@GetMapping("/hello")

public String hello() {

return "Hello, I am provider, port: " + port;

}

}

最后,如果需要我们可以重写健康检查接口,用于反馈服务的状态和可用性。这里我们需要引入 Actuator。

@Component

public class DatabaseHealthIndicator implements HealthIndicator {

@Override

public Health health() {

if (isDatabaseConnectionOK()) {

return Health.up().build();

} else {

return Health.down().withDetail("Error Code", "DB-001").build();

}

}

private boolean isDatabaseConnectionOK() {

// 检查数据库连接、缓存等

return true;

}

}

这样,我们就完成了一个简单的服务提供者应用,并且可以通过北极星来实现服务注册与发现。

接下来,我们需要创建一个服务消费者应用,并且也添加北极星的依赖和配置信息。

然后,使用 RestTemplate 来调用服务提供者的接口,例如:

@SpringBootApplication

public class ConsumerApplication {

public static void main(String[] args) {

SpringApplication.run(ConsumerApplication.class, args);

}

@Bean

@LoadBalanced // 开启负载均衡

public RestTemplate restTemplate() {

return new RestTemplate();

}

@RestController

public class ConsumerController {

@Autowired

private RestTemplate restTemplate;

@GetMapping("/hello")

public String hello() {

// 使用服务名来调用服务提供者的接口

return restTemplate.getForObject("<http://provider/hello>", String.class);

}

}

}

这里我们使用了 @LoadBalanced 注解来开启负载均衡功能,并且使用服务名 provider 来调用服务提供者的接口。

这样,我们就完成了一个简单的服务消费者应用,并且可以通过北极星来实现服务注册与发现。

接下来,我们就可以通过以下步骤来实现优雅上线的过程:

  • 在发布新版本的服务提供者应用时,先启动新版本的应用实例,但是不向注册中心注册服务,或者让健康检查接口返回不健康的状态,这样就不会有新的请求进入新版本的应用实例。这可以通过配置或代码来实现,例如:
# 不向注册中心注册服务

spring.cloud.polaris.discovery.register=false
// 让健康检查接口返回不健康的状态

this.isHealthy = false;
  • 在新版本的应用实例完成初始化操作后,再向注册中心注册服务,或者让健康检查接口返回健康的状态,这样就可以让新的请求被路由到新版本的应用实例上。这可以通过配置或代码来实现,例如:
# 向注册中心注册服务

spring.cloud.polaris.discovery.register=true
// 让健康检查接口返回健康的状态

this.isHealthy = true;

这样,就可以实现优雅上线的过程,保证正在处理的请求不会被中断,而新的请求会被路由到新版本的应用上。

不过,如果对优雅上线的极致要求不高,北极星本身就是支持优雅上线的,无须做额外的操作。因为北极星的逻辑是,当 Spring 的 Bean 全部加载完成后,Controller 能访问后才会去注册服务。所以,在绝大多数的场景下,它已经满足了优雅上线的要求。

服务预热

服务预热是指在服务上线之前,先让服务处于一个运行状态,让其加载必要的资源、建立连接等,以便在服务上线后能够快速响应请求。如下图所示。

在流量较大情况下,刚启动的服务直接处理大量请求可能由于应用内部资源初始化不彻底从而出现请求阻塞、报错等问题。此时通过服务预热,在服务刚启动阶段通过小流量帮助服务在处理大量请求前完成初始化,可以帮助发现服务上线后可能存在的问题,例如资源不足、连接数过多等,从而及时进行调整和优化,确保服务的稳定性和可靠性。

图片

云原生 API 网关实现服务预热

云原生 API 网关是腾讯云基于开源微服务网关推出的一款高性能高可用的云上网关托管产品。我们可以通过简单的几个配置就能实现服务预热。

首先我们在网关新建后端服务的时候,可以打开下图中的慢启动开关。同时可以设置慢启动的时间。

图片

开启后,服务端有新的服务节点上线后,会在设置的慢启动的时间内,将新节点的权重从1逐步增加到目标值。这个新节点的流量会慢慢增加。

如果有多个新增节点,那所有新增的节点都会慢启动。

针对后端来源是 K8S 服务 、注册中心、IP 列表的服务都可以实现慢启动,也就是服务预热。

优雅下线

图片

无损下线、优雅下线都是同一个意思。都是为了避免服务下线的时候由于请求没有处理完导致请求失败的情况。

优雅下线的方法

无损下线的一些常用的工具或框架有:

  • Dubbo-go:支持多种注册中心、负载均衡、容灾策略等,可以实现优雅上下线的设计与实践。

  • Spring Cloud:提供了多种组件来实现服务的配置、路由、监控、熔断等,可以通过监听 ContextClosedEvent 事件来实现优雅下线的逻辑。

  • Docker:可以通过 Docker Stop 或 Docker Kill 命令来停止容器,前者会发送 SIGTERM 信号给容器的 PID1 进程,后者会发送 SIGKILL 信号。如果程序能响应 SIGTERM 信号,就可以实现优雅下线的操作。

Spring Cloud 优雅下线的原理

ContextClosedEvent 是 Spring 容器在关闭时发布的一个事件,可以通过实现 ApplicationListener 接口来监听这个事件,并在 onApplicationEvent 方法中执行一些自定义的逻辑。

对于 Spring Cloud 中的微服务来说,当收到 ContextClosedEvent 事件时,可以做以下几件事情:

  • 从注册中心注销当前服务,这样就不会再有新的请求进入。

  • 拒绝或者延迟新的请求,这样就可以保证正在处理的请求不会被中断。

  • 等待一段时间,让旧的请求处理完毕,或者超时。

  • 关闭服务,释放资源。

这样就可以实现优雅下线的逻辑,避免因为服务的变更而造成流量的中断或错误。

Spring Boot 优雅下线的 Demo

在旧版本里面,我们需要实现 TomcatConnectorCustomizer 和 ApplicationListener接口,然后就可以在 Customize 方法中获取到 Tomcat 的 Connector 对象,并在 onApplicationEvent 方法中监听到 Spring 容器的关闭事件。

在2.3及以后版本,我们只需要在 application.yml 中添加几个配置就能启用优雅关停了。

# 开启优雅停止 Web 容器,默认为 IMMEDIATE:立即停止

server:

shutdown: graceful

# 最大等待时间

spring:

lifecycle:

timeout-per-shutdown-phase: 30s

这个开关的具体实现逻辑在我们在 GracefulShutdown 里。

然后我们需要添加 Actuator 依赖,然后在配置中暴露 Actuator 的 Shutdown 接口。

# 暴露 shutdown 接口

management:

endpoint:

shutdown:

enabled: true

endpoints:

web:

exposure:

include: shutdown

这个时候,我们调用 http://localhost:8080/actuator/shutdown 就可以执行优雅关停了,它会返回如下内容:

{

"message": "Shutting down, bye..."

}

优缺点

我觉得这种方法有以下的优点和缺点:

优点

  • 简单易用,只需要实现两个接口,就可以实现优雅下线的逻辑。

  • 适用于 Tomcat 作为内嵌容器的 Spring Boot 应用,不需要额外的配置或依赖。

  • 可以保证正在处理的请求不会被中断,而新的请求不会进入,避免了服务的变更造成流量的中断或错误。

缺点:

  • 只适用于 Tomcat 作为内嵌容器的 Spring Boot 应用,如果使用其他的容器或部署方式,可能需要另外的实现。

  • 需要等待一定的时间,让正在处理的请求完成或超时,这可能会影响服务的停止速度和资源的释放。

  • 如果正在处理的请求过多或过慢,可能会导致线程池无法优雅地关闭,或者超过系统的终止时间,造成强制关闭。

Docker 优雅下线的 Demo

这里用一个简单的 JS 应用来演示 Docker 实现无损下线的过程。

首先,我们需要创建一个 Dockerfile 文件,用于定义一个简单的应用容器,代码如下:

# 基于 node:14-alpine 镜像

FROM node:14-alpine

# 设置工作目录

WORKDIR /app

# 复制 package.json 和 package-lock.json 文件

COPY package*.json ./

# 安装依赖

RUN npm install

# 复制源代码

COPY . .

# 暴露 3000 端口

EXPOSE 3000

# 启动应用

CMD [ "node", "app.js" ]

然后,我们需要创建一个 app.js 文件,用于定义一个简单的 Web 应用,代码如下:

// 引入 express 模块

const express = require('express');

// 创建 express 应用

const app = express();

// 定义一个响应 /hello 路径的接口

app.get('/hello', (req, res) => {

// 返回 "Hello, I am app" 字符串

res.send('Hello, I am app');

});

// 监听 3000 端口

app.listen(3000, () => {

// 打印日志信息

console.log('App listening on port 3000');

});

接下来,我们需要在终端中执行以下命令,来构建和运行我们的应用容器,并查看页面结果。

# 构建镜像,命名为 app:1.0.0

docker build -t app:1.0.0 .

# 运行容器,命名为 app-1,映射端口为 3001:3000

docker run -d --name app-1 -p 3001:3000 app:1.0.0

# 查看容器运行状态和端口映射信息

docker ps

CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES

a8a9f9f7c6c4 app:1.0.0 "docker-entrypoint.s…" 10 seconds ago Up 9 seconds 0.0.0.0:3001->3000/tcp app-1

# 在浏览器中访问 <http://localhost:3001/hello> ,可以看到返回 "Hello, I am app" 字符串

这个时候假设我们要发布一个新版本的应用,我们需要修改 app.js 文件中的代码,把返回的字符串修改为 “Hello, I am app v2”。

然后,我们需要在终端中执行以下命令,来构建和运行新版本的应用容器:

# 构建镜像,命名为 app:2.0.0

docker build -t app:2.0.0 .

# 运行容器,命名为 app-2,映射端口为 3002:3000

docker run -d --name app-2 -p 3002:3000 app:2.0.0

# 查看容器运行状态和端口映射信息

docker ps

CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES

b7b8f8f7c6c4 app:2.0.0 "docker-entrypoint.s…" 10 seconds ago Up 9 seconds 0.0.0.0:3002->3000/tcp app-2

a8a9f9f7c6c4 app:1.0.0 "docker-entrypoint.s…" 2 minutes ago Up 2 minutes 0.0.0.0:3001->3000/tcp app-1

# 在浏览器中访问 <http://localhost:3002/hello> ,可以看到返回 "Hello, I am app v2" 字符串

接下来,需要优雅地下线旧版本的应用容器,让它完成正在处理的请求,然后停止接收新的请求,最后退出进程。

# 向旧版本的应用容器发送 SIGTERM 信号,让它优雅地终止

docker stop app-1

# 查看容器运行状态和端口映射信息

docker ps

CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES

b7b8f8f7c6c4 app:2.0.0 "docker-entrypoint.s…" 2 minutes ago Up 2 minutes 0.0.0.0:3002->3000/tcp app-2

# 在浏览器中访问 <http://localhost:3001/hello> ,可以看到无法连接到服务器的错误

这样,我们就实现了通过 Docker 来做优雅下线的过程,保证正在处理的请求不会被中断,而新的请求会被路由到新版本的应用上。

这里主要用到了 Docker Stop 命令。Docker Stop 命令会向容器发送 SIGTERM 信号,这是一种优雅终止进程的方式,它会给目标进程一个清理善后工作的机会,比如完成正在处理的请求,释放资源等。如果目标进程在一定时间内(默认为 10 秒)没有退出,Docker Stop 命令会再发送 SIGKILL 信号,强制终止进程。

所以,使用 Docker Stop 命令能实现优雅下线的前提是,容器中的应用能够正确地响应 SIGTERM 信号,并在收到该信号后执行清理工作。如果容器中的应用忽略了 SIGTERM 信号,或者在清理工作过程中出现异常,那么 Docker Stop 命令就无法实现优雅下线的效果。

让容器中的应用正确地响应 SIGTERM 信号的方法,主要取决于容器中的 1 号进程是什么,以及它如何处理信号。如果容器中的 1 号进程就是应用本身,那么应用只需要在代码中为 SIGTERM 信号注册一个处理函数,用于执行清理工作和退出进程。例如,在 Node.js 中,可以这样写:

// 定义一个处理 SIGTERM 信号的函数

function termHandler() {

// 执行清理工作

console.log('Cleaning up...');

// 退出进程

process.exit(0);

}

// 为 SIGTERM 信号注册处理函数

process.on('SIGTERM', termHandler);

北极星的优雅下线

北极星的心跳默认是5秒维持一次,客户端的缓存默认是2秒刷新一次。理论上,在极致情况下,服务下线会有2秒的不可用时间。但客户端都有重试机制,且大部分客户端的超时时间都是大于2秒的。因此大部分情况下,服务在北极星下线是不会造成业务感知的。

北极星的优雅下线有多种方式。其中上面的 Spring Boot 与 Docker 的方式是其中两种。

另外一种是可以在服务下线的时候,在 PreStop 的时候去做服务隔离与反注册。

这样的隔离操作可以手动做,也可以通过脚本来自动做。

[外链图片转存中…(img-SxCnVoCD-1689158362089)]

如上图,被隔离的实例将不会被主调方发现,这样就不会有新的需求进来,在处理完成现有的请求后,就可以执行下线操作了。

总结

图片

优雅上下线的价值

在微服务实践中,实现优雅上下线能给我们带来以下好处:

  1. 最小化服务中断:通过优雅上下线,可以最小化服务中断的时间和影响范围,从而确保服务的可用性和稳定性。

  2. 避免数据丢失:优雅下线可以确保正在处理的请求能够完成,避免数据丢失和请求失败。

  3. 提高用户体验:优雅上下线可以确保用户在使用服务时不会遇到任何中断或错误,从而提高用户体验和满意度。

  4. 简化部署流程:通过使用自动化工具和流程,可以简化部署流程,减少人工干预和错误,提高部署效率和质量。

  5. 提高可维护性:通过使用监控和日志记录工具,可以及时发现和解决问题,提高服务的可维护性和可靠性。

这些好处可以帮助企业提高服务质量和效率,提升用户满意度和竞争力。

优雅上下线的挑战

但同时,优雅上下线也面临一些挑战:

  1. 复杂性增加:微服务架构通常由多个服务组成,每个服务都有自己的生命周期和依赖关系,因此优雅上下线需要考虑多个服务之间的交互和协调,增加了系统的复杂性。

  2. 部署流程复杂:优雅上下线需要使用自动化工具和流程,这需要投入大量的时间和资源来构建和维护,增加了部署流程的复杂性。

  3. 数据一致性问题:优雅下线需要确保正在处理的请求能够完成,但这可能会导致数据一致性问题,需要采取措施来解决这个问题。

  4. 人员技能要求高:微服务架构需要具备更高的技术水平和技能,需要拥有更多的开发和运维经验,这对企业的人员要求较高。

综上所述,企业需要认真考虑这些挑战,并采取相应的措施来解决这些问题,以确保在微服务实践中更好的落地优雅上下线。

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

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

相关文章

Win10下安装TensorRT

Win10下安装TensorRT 前言相关介绍Win10下安装TensorRT查看cuda版本下载tensorrt8.xx版本&#xff0c;适用于Windows的cuda11.x的版本解压下载好的压缩包使用pip下载wheel文件遇到新问题解决方法 测试TensorRT是否安装成功 参考 前言 由于本人水平有限&#xff0c;难免出现错漏…

WAS 9.0 ND 命令行安装-基于LINUX 8

WAS 9.0 安装文件准备如下&#xff1a; gtk.x86_64_1.8.9004.20190423_2015.zip ----IM安装源文件 sdk.repo.8035.java8.linux.zip ----JAVA安装源文件 was.repo.90501.nd.zip ----WAS安装源文件 …

深度学习(27)——YOLO系列(6)

深度学习&#xff08;27&#xff09;——YOLO系列&#xff08;6&#xff09; 嗨&#xff0c;好久不见&#xff0c;昨天结束了yolov7的debug过程&#xff0c;真的在尽力句句理解&#xff0c;我想这应该是我更新的yolo系列的最后一篇&#xff0c;但是仅限于yolo&#xff0c;dete…

Day43: 123.买卖股票的最佳时机III,188.买卖股票的最佳时机IV

目录 123.买卖股票的最佳时机III 思路 188.买卖股票的最佳时机IV 思路 123.买卖股票的最佳时机III 123. 买卖股票的最佳时机 III - 力扣&#xff08;LeetCode&#xff09; 思路 1. 确定dp数组及其下标含义 一天一共就有五个状态&#xff0c; 没有操作 &#xff08…

白皮书精彩章节:从冬奥会看我们如何做到与“双碳”目标偕行

以下案例来自于《数字孪生世界白皮书&#xff08;2023版&#xff09;》 领取方式&#xff1a;公众号「EasyV数字孪生」后台回复「白皮书」即可领取&#xff01; 一、“双碳”提出背景 1、正式提出 在2020年9月22日75届联合国大会上&#xff0c;中国首次在国际公开场合提出…

Vue计算属性与监听器

文章目录 计算属性配置项 computedHTML 结构Vue 实例数据方法计算属性绑定数据和方法完整代码 监听器配置项 watch简单类型写法深度监听写法 计算属性配置项 computed 使用 Vue 实现一个商品价格计算器&#xff0c;设置一个初始单价&#xff0c;初始数量为 1&#xff0c;用户可…

开源外卖点餐系统源码:提升餐饮行业数字化转型

随着数字化时代的到来&#xff0c;餐饮行业正积极寻求数字化转型的方式来适应市场需求。开源外卖点餐系统源码成为推动餐饮行业数字化转型的有力工具。本文将介绍一个开源外卖点餐系统的源码&#xff0c;并解析其中的关键代码&#xff0c;展示如何利用开源源码来提升餐饮行业的…

漏洞攻击 --- TCP -- 半开攻击、RST攻击

TCP半开攻击&#xff08;半连接攻击&#xff09; --- syn攻击 &#xff08;1&#xff09;定义&#xff1a; sys 攻击数据是DOS攻击的一种&#xff0c;利用TCP协议缺陷&#xff0c;发送大量的半连接请求&#xff0c;耗费CPU和内存资源&#xff0c;发生在TCP三次握手中。 A向B…

2023年为何最卷的IT行业仍是很多人的首选?

最近这段时间&#xff0c;“IT行业崩盘了”、“前端已死&#xff0c;后端已亡”这些言论在网络中甚嚣尘上&#xff0c;引起了激烈的讨论。 对此&#xff0c;小编只想说&#xff1a;别再看了&#xff01;这不妥妥的传播焦虑吗&#xff1f;他们怎么只告诉你IT行业完了&#xff…

前端预览pdf文件

在前端开发中&#xff0c;很多时候我们需要进行pdf文件的预览操作&#xff0c;下面给出几种常见的预览pdf文件的方法&#xff1a; 一&#xff1a;直接浏览器打开 如果项目对pdf的预览功能要求不高&#xff0c;只是要求能够看的话&#xff0c;可以直接在浏览器上打开pdf文件的…

浅谈 java 虚拟机 JVM

前言 小亭子正在努力的学习编程&#xff0c;接下来将开启JavaEE的学习~~ 分享的文章都是学习的笔记和感悟&#xff0c;如有不妥之处希望大佬们批评指正~~ 同时如果本文对你有帮助的话&#xff0c;烦请点赞关注支持一波, 感激不尽~~ 目录 前言 JVM中的内存划分 JVM的类加载机制…

今天我们要向您介绍的是一款来自厂家的劲道半干面 - 味尚拉面

尊敬的顾客您好&#xff0c;感谢您选择我们的电商平台。今天我们要向您介绍的是一款来自厂家的劲道半干面 - 味尚拉面。 味尚拉面是一款经典的面食品牌&#xff0c;以选用高品质面粉和精选优质食材为特点&#xff0c;采用独特工艺制作而成。其中&#xff0c;味尚拉面的半干面更…

ChatGPT 最佳实践指南之:给 GPT 足够的时间“思考”

Give GPTs time to "think" 给予 GPT 足够的时间“思考” If asked to multiply 17 by 28, you might not know it instantly, but can still work it out with time. Similarly, GPTs make more reasoning errors when trying to answer right away, rather than ta…

Modbus TCP/BACnet IP/MQTT物联网网关IOT-810介绍及其典型应用

伴随着计算机技术以及互联网的发展&#xff0c;物联网这个概念已经逐渐进入我们的日常生活&#xff0c;例如智能泊车&#xff0c;智能家居&#xff0c;智能照明&#xff0c;智能楼宇等。智能楼宇是将传统的楼宇自控系统与物联网技术相融合&#xff0c;把系统中常见的传感器、设…

克服 ClickHouse 运维难题:ByteHouse 水平扩容功能上线

前言 对于分析型数据库产品&#xff0c;通过增加服务节点实现集群水平扩容&#xff0c;并提升集群性能和容量&#xff0c;是运维的必要手段。 但是对于熟悉 ClickHouse 的工程师而言&#xff0c;听到“扩容”二字一定会头疼不已。开源 ClickHouse 的 MPP 架构导致扩容成本高&…

【前缀和优化DP】ABC 222D

虽然很简单&#xff0c;但是统一一下板子&#xff0c;以防写错 D - Between Two Arrays (atcoder.jp) 题意&#xff1a; 思路&#xff1a; 直接DP即可 Code&#xff1a; #include <bits/stdc.h>#define int long longusing namespace std;const int mxn3e310; const…

ASP.NET Website 项目 .NET Framework 4.0 ~ .NET Framework 4.8支持c#哪些版本(Website)

本文讲的是Website网站项目&#xff0c;由于维护老项目Website .net framework4.0&#xff0c;遇到c#6.0语法不支持。便做了点记录 ASP.NET Website 项目 .NET Framework 4.0、 .NET Framework 4.5、 .NET Framework 4.6、 .NET Framework 4.8都支持c#哪些版本&#xff1f; 下面…

使用一行css实现黑白色主题皮肤的切换

很多网站都有切换主题的效果 比如如下所示 示例代码 <template><div class"css-switch-theme"><el-switchchange"hanldeSwitchTheme"v-model"themValue"active-text"暗黑"inactive-text"白色"active-color&q…

文件传输越来越频繁,如何选择高速文件传输解决方案

随着云计算、大数据等技术的发展和人们对文件传输速度的要求不断提高&#xff0c;高速文件传输成为个人和企业之间必不可少的需求。在这个背景下&#xff0c;如何实现安全、稳定、高效的文件传输就成为了一个热门话题。本文将从以下几个方面&#xff0c;详细介绍 高速文件传输解…

SpringBoot 集成 Mybatis

SpringBoot 集成 Mybatis 详细教程 &#xff08;只有操作&#xff0c;没有理论&#xff0c;仅供参考学习&#xff09; 一、操作部分 1. 准备数据库 1.1 数据库版本&#xff1a; C:\WINDOWS\system32>mysql -V mysql Ver 8.0.25 for Win64 on x86_64 (MySQL Community …