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

导语

本文介绍了微服务优雅上下线的实践方法及原理,包括适用于 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 服务地址}:8091namespace: default

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

@RestControllerpublic class ProviderController {@Value("${server.port}")private int port;@GetMapping("/hello")public String hello() {return "Hello, I am provider, port: " + port;}}

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

@Componentpublic class DatabaseHealthIndicator implements HealthIndicator {@Overridepublic Health health() {if (isDatabaseConnectionOK()) {return Health.up().build();} else {return Health.down().withDetail("Error Code", "DB-001").build();}}private boolean isDatabaseConnectionOK() {// 检查数据库连接、缓存等return true;}}

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

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

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

@SpringBootApplicationpublic class ConsumerApplication {public static void main(String[] args) {SpringApplication.run(ConsumerApplication.class, args);}@Bean@LoadBalanced // 开启负载均衡public RestTemplate restTemplate() {return new RestTemplate();}@RestControllerpublic class ConsumerController {@Autowiredprivate 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: trueendpoints: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.0docker build -t app:1.0.0 .# 运行容器,命名为 app-1,映射端口为 3001:3000docker run -d --name app-1 -p 3001:3000 app:1.0.0# 查看容器运行状态和端口映射信息docker psCONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMESa8a9f9f7c6c4 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.0docker build -t app:2.0.0 .# 运行容器,命名为 app-2,映射端口为 3002:3000docker run -d --name app-2 -p 3002:3000 app:2.0.0# 查看容器运行状态和端口映射信息docker psCONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMESb7b8f8f7c6c4 app:2.0.0 "docker-entrypoint.s…" 10 seconds ago Up 9 seconds 0.0.0.0:3002->3000/tcp app-2a8a9f9f7c6c4 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 psCONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMESb7b8f8f7c6c4 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.mzph.cn/news/1103.shtml

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

相关文章

Flask_实现token鉴权

目录 1、安装依赖 2、实现代码 3、测试 源码等资料获取方法 1、安装依赖 pip install flask pip install pycryptodome 2、实现代码 import random import string import time import base64from functools import wrapsfrom flask import Flask, jsonify, session, req…

RabbitMQ如何保证消息的可靠性6000字详解

RabbitMQ通过生产者、消费者以及MQ Broker达到了解耦的特点&#xff0c;实现了异步通讯等一些优点&#xff0c;但是在消息的传递中引入了MQ Broker必然会带来一些其他问题&#xff0c;比如如何保证消息在传输过程中可靠性&#xff08;即不让数据丢失&#xff0c;发送一次消息就…

学习babylon.js --- [2] 项目工程搭建

本文讲述如何搭建babylonjs的项目工程。 一 准备 首先创建一个目录叫MyProject&#xff0c;然后在这个目录里再创建三个目录&#xff1a;dist&#xff0c;public和src&#xff0c;如下&#xff0c; 接着在src目录里添加一个文件叫app.ts&#xff0c;本文使用typescript&#…

docker数据卷权限管理--理论和验证

一、Docker容器中用户权限管理 Linux系统的权限管理是由uid和gid负责&#xff0c;Linux系统会检查创建进程的uid和gid&#xff0c;以确定它是否有足够的权限修改文件&#xff0c;而非是通过用户名和用户组来确认。 同样&#xff0c;在docker容器中主机上运行的所有容器共享同一…

【kubernetes系列】Kubernetes之配置dashboard安装使用

Kubernetes之配置dashboard 概述 Dashboard 是基于网页的 Kubernetes 用户界面。 你可以使用 Dashboard 将容器应用部署到 Kubernetes 集群中&#xff0c;也可以对容器应用排错&#xff0c;还能管理集群资源。 你可以使用 Dashboard 获取运行在集群中的应用的概览信息&#x…

【单例模式】—— 每天一点小知识

&#x1f4a7; 单例模式 \color{#FF1493}{单例模式} 单例模式&#x1f4a7; &#x1f337; 仰望天空&#xff0c;妳我亦是行人.✨ &#x1f984; 个人主页——微风撞见云的博客&#x1f390; &#x1f433; 《数据结构与算法》专栏的文章图文并茂&#x1f995;生动形…

LiveGBS流媒体平台GB/T28181功能-作为上级平台对接海康大华华为宇视等下级平台监控摄像机NVR硬件执法仪等GB28181设备

LiveGBS作为上级平台对接海康大华华为宇视等下级平台监控摄像机NVR硬件执法仪等GB28181设备 1、背景说明2、部署国标平台2.1、安装使用说明2.2、服务器网络环境2.3、信令服务配置 3、监控摄像头设备接入3.1、海康GB28181接入示例3.2、大华GB28181接入示例3.3、华为IPC GB28181接…

SpringBoot整合ZooKeeper完整教程

目录 ZooKeeper简单介绍 一、安装zookeeper 二、springboot整合zookeeper ZooKeeper简单介绍 zookeeper是为分布式应用程序提供的高性能协调服务。zookeeper将命名、配置管理、同步和组服务等常用服务公开在一个简单的接口中&#xff0c;因此用户无需从头开始编写这些服务。可…

Android GridPager实战,从RecyclerView to ViewPager

这个简单的的案例展示了如何从RecyclerView to ViewPager&#xff0c;以网上的公开图片为样例。 安卓开发中从RecyclerView 到 ViewPager demo运行结果demo项目工程目录结构关键代码 MainActivity关键代码GridFragment关键代码ImageFragment关键代码ImagePagerFragment关键布局…

CSS---CSS面试题

目录 1.盒模型 2.offsetHeight /clientheight/scrollHeight 3.left与offsetLeft 4.对BFC规范的理解 5.解决元素浮动导致的父元素高度塌陷的问题 6.CSS样式的先级 7.隐藏页面元素 8.display: none 与 visibility: hidden 的区别 9.页面引入样式时&#xff0c;使用link与import有…

C++学习——类和对象(一)

C语言和C语言最大的区别在于在C当中引入了面向对象的编程思想&#xff0c;想要完全了解c当中的类和对象&#xff0c;就要从头开始一点一点的积累并学习。 一&#xff1a;什么是面向对象编程 我们之前学习的C语言属于面向过程的编程方法。举一个简单的例子来说&#xff1a;面向过…

使用npm和nrm查看源和切换镜像

一、使用npm查看当前源、切换淘宝镜像、切换官方源 &#xff08;1&#xff09;npm查看当前源&#xff1a; npm get registry &#xff08;2&#xff09;npm设置淘宝镜像源&#xff1a; npm config set registry http://registry.npm.taobao.org &#xff08;3&#xff09;n…

【运维工程师学习三】Linux中Shell脚本编写

【运维工程师学习三】shell编程 Shell程序分类1、系统中sh命令是bash的软链接2、Shell脚本标准格式之文件后缀3、Shell脚本标准格式之文件内容首行4、Shell脚本的运行方法一、作为可执行程序解释 二、作为解释器&#xff08;bash&#xff09;参数 5、find、grep、xargs、sort、…

网络协议与攻击模拟-17-DNS协议-报文格式

二、DNS 查询 客户机想要访问www.baidu.com&#xff0c;根据自己的 TCP / IP 参数&#xff0c;向自己的首选 DNS 服务器发送 DNS 请求 首选 DNS 收到客户机的请求后&#xff0c;会去查询自己的区域文件&#xff0c;找不到www.baidu.com的 IP 地址信息&#xff08;将请求转发到…

MYSQL 5.7.17 安装版 的配置文件

解压版解压后都有 my.ini配置文件&#xff0c;安装版要查找这个配置文件可以查看 MYSQL Workbench --> 左侧 INSTANCE --> Options File &#xff0c;然后可以看到底部 Configuration File所处的位置&#xff0c;即为my.ini的路径。

Jupyter notebook添加与删除kernel

目录 1 添加虚拟环境的kernel 2 删除jupyter notebook已有的kernal 3 切换内核与查看当前内核 4 添加C语言的kernel 5 添加python2的kernel 6 添加java语言的kernel 6.1 sudo apt install default-jre 6.2 下载并安装 ijava 6.3 sudo apt install openjdk-11…

TortoiseGit 入门指南05:推送和拉取

本节所讲内容均涉及到 远端版本库。 版本库 的概念在《TortoiseGit 入门指南02&#xff1a;创建和克隆仓库》中提到过&#xff0c;它是工作目录下面的一个名为 .git 的隐藏目录&#xff0c;我们每一次提交、每一个分支都会保存在版本库中。这个版本库就在我们电脑上的某个文件…

鸽了百万用户四年的赛博皮卡终于要来啦

作者 | Amy 编辑 | 德新 本月15号&#xff0c;特斯拉官方宣布&#xff0c;第一辆 赛博皮卡已在特斯拉得州工厂下线。 而就在本月初&#xff0c;马斯克还发推预热了一波&#xff0c;「开着赛博皮卡在奥斯汀&#xff08;特斯拉得州工厂所在地&#xff09;溜了一圈&#xff01…

THREE.JS镜头随鼠标晃动效果

为了让动画更灵活并且简单 借助gsap让其具有更多可能&#xff0c;在未来更容易扩充其他动效 gsap Dom跟随鼠标移动 gsap.quickTo() 首先要监听鼠标移动&#xff0c;并且将移动的值转换到 -1 和 1 之间 方便处理 private mousemove(e: MouseEvent) {const x (e.clientX / inner…

华为配置LLDP基本功能

华为配置LLDP基本功能 1.什么是lldp协议 定义 LLDP(Link Layer Discovery Protocol)是IEEE 802.1ab中定义的链路层发现协议。LLDP是一种标准的二层发现方式,可以将本端设备的管理地址、设备标识、接口标识等信息组织起来,并发布给自己的邻居设备,邻居设备收到这些信息后将…