Spring Cloud Kubernetes 实践 服务注册发现、服务动态配置

一、Spring Cloud Kubernetes

随着云计算和微服务架构的不断发展,k8sSpring Cloud成为了当今技术领域的两大热门话题。k8s作为一个开源的容器编排平台,已经在自动化部署、扩展和管理方面取得了巨大的成功,而Spring Cloud则以其丰富的生态和语言亲和性,为Java开发者提供了便利的微服务框架支持。在现如今的技术趋势下,两者融合已经成为了必要的方向。

首先,k8s的火热程度不言而喻。它不仅支持多种编程语言和框架,还提供了强大的自动化部署、扩展和管理能力,使得开发者可以根据自己的需求进行定制。此外,k8s的强大自动化能力也为开发、测试和运维团队带来了便利。

Spring Cloud作为微服务框架,提供了丰富的组件和工具,如服务注册与发现、负载均衡、熔断器等,可以帮助开发者快速构建微服务应用。并且针对Java开发者,提供了许多开箱即用的功能。然而,Spring Cloud主要适用于Java语言,对其他语言的支持有限,且在某些情况下可能显得有些重量级。

在面对这样的技术选择时,将k8sSpring Cloud融合使用是一个理想的选择。充分发挥它们各自的优势,构建更加稳定和高效的微服务架构。利用k8s进行容器的编排和部署,实现自动化管理。例如,通过k8sService机制,可以实现服务发现和负载均衡;通过k8sConfigMapSecrets,可以实现配置管理;通过k8sIngressGateway,可以实现智能路由。

在如此背景下 Sping Cloud 提供了 Spring Cloud Kubernetes 子项目,旨在将Spring Cloudk8s 结合起来。Spring Cloud Kubernetes 在保持原有开发模式的同时,几乎无感的做到对接 k8s ,使得在k8s环境中运行Spring Cloud应用程序变得更加容易和高效。例如:允许应用程序自动发现k8s集群中的其他服务,从而使得服务间通信更加便捷,更加高效实现服务发现与注册。还支持从k8sConfigMapSecrets中读取配置信息,并自动更新应用程序中的配置属性。

官方文档如下:

https://docs.spring.io/spring-cloud-kubernetes/reference/3.1-SNAPSHOT/index.html

下面使用 Spring Cloud Kubernetes 搭建两个 SpringBoot 服务,分别作为服务提供者、服务消费者实现服务的注册和发现,并且在消费者端使用 ConfigMap 作为动态配置。

角色replicas类型动态配置
provider2Deploymentconsumerconfig
consumer2Deployment

新建一个 Maven 项目作为父组件,在该项目中再创建两个SpringBoot Model

首先在父 pom 中增加依赖,声明SpringBootStringCloud 的版本:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>org.example</groupId><artifactId>nacos-parent</artifactId><version>1.0-SNAPSHOT</version><packaging>pom</packaging><name>service-parent</name><url>http://maven.apache.org</url><properties><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><spring-boot.version>2.3.7.RELEASE</spring-boot.version><spring-cloud.version>Hoxton.SR9</spring-cloud.version></properties><modules><module>provider-service</module><module>consumer-service</module></modules><dependencyManagement><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-dependencies</artifactId><version>${spring-boot.version}</version><type>pom</type><scope>import</scope></dependency><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-dependencies</artifactId><version>${spring-cloud.version}</version><type>pom</type><scope>import</scope></dependency></dependencies></dependencyManagement>
</project>

二、服务提供者搭建

新建 SpringBoot model ,修改 pom 内容如下:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>com.example</groupId><artifactId>provider-service</artifactId><version>0.0.1-SNAPSHOT</version><name>provider-service</name><description>provider-service</description><parent><groupId>org.example</groupId><artifactId>nacos-parent</artifactId><version>1.0-SNAPSHOT</version></parent><properties><java.version>1.8</java.version><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-kubernetes</artifactId></dependency></dependencies><build><plugins><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><version>3.8.1</version><configuration><source>1.8</source><target>1.8</target><encoding>UTF-8</encoding></configuration></plugin><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><version>${spring-boot.version}</version><configuration><mainClass>com.example.providerservice.ProviderServiceApplication</mainClass></configuration><executions><execution><id>repackage</id><goals><goal>repackage</goal></goals></execution></executions></plugin></plugins></build></project>

修改 application.yml

server:port: 8091spring:application:name: provider

启动类增加 @EnableDiscoveryClient 注解:

@EnableDiscoveryClient
@SpringBootApplication
public class ProviderServiceApplication {public static void main(String[] args) {SpringApplication.run(ProviderServiceApplication.class, args);}}

创建一个测试接口,后面在服务消费端通过 OpenFeign 调用:

@RestController
public class ProviderController {/*** 提供服务接口*/@GetMapping("/rpc")public String rpc() {return "provider from : " + getCurrentHostName();}/*** 获取当前系统名称*/private String getCurrentHostName(){try {InetAddress inetAddress = InetAddress.getLocalHost();return inetAddress.getHostName();} catch (UnknownHostException e) {e.printStackTrace();}return null;}}

建立 Dockerfile 文件,内容如下:

FROM java:8
MAINTAINER bxc
WORKDIR /app
ADD target/provider-service-0.0.1-SNAPSHOT.jar /app/app.jar
CMD ["java", "-jar", "app.jar"]

在这里插入图片描述

三、服务消费者搭建

消费者端通过 OpenFeign 调用服务提供者端的接口信息,并使用 ConfigMap 动态配置。

新建 SpringBoot model ,修改 pom 内容如下:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>com.example</groupId><artifactId>consumer-service</artifactId><version>0.0.1-SNAPSHOT</version><name>consumer-service</name><description>consumer-service</description><parent><groupId>org.example</groupId><artifactId>nacos-parent</artifactId><version>1.0-SNAPSHOT</version></parent><properties><java.version>1.8</java.version><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-kubernetes</artifactId></dependency><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-kubernetes-ribbon</artifactId></dependency><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-kubernetes-config</artifactId></dependency><!--openfeign--><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-openfeign</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-actuator</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-actuator-autoconfigure</artifactId></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></dependency></dependencies><build><plugins><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><version>3.8.1</version><configuration><source>1.8</source><target>1.8</target><encoding>UTF-8</encoding></configuration></plugin><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><version>${spring-boot.version}</version><configuration><mainClass>com.example.consumerservice.ConsumerServiceApplication</mainClass></configuration><executions><execution><id>repackage</id><goals><goal>repackage</goal></goals></execution></executions></plugin></plugins></build></project>

修改配置文件,注意不要写在application.yml 中,新建一个bootstrap.yml文件,用来在程序引导时执行:

server:port: 8080spring:application:name: consumercloud:kubernetes:config:name: ${CONFIG_NAME:consumerconfig}namespace: ${NAMESPACE:cloud}reload:# 是否自动更新enabled: true# 监控configMap变化monitoring-config-maps: true# 监控secrets变化monitoring-secrets: true# 加载策略# refresh:只重新加载用@ConfigurationProperties或@RefreshScope注释的配置bean。此重新加载级别利用了Spring Cloud Context的刷新功能。# restart_context:整个Spring ApplicationContext被优雅地重新启动。使用新配置重新创建bean。为了使重启上下文功能正常工作,您必须启用并公开restart端点# shutdown:关闭Spring ApplicationContext以激活容器的重新启动。使用此级别时,请确保所有非守护进程线程的生命周期都绑定到ApplicationContext,并且已配置复制控制器或副本集以重新启动pod。strategy: restart_context#更新配置信息的模式:polling:主动拉取,event:事件通知mode: event#如果主动拉取,拉取的时间间隔,单位毫秒#period: 500management:endpoint:restart:enabled: trueendpoints:web:exposure:include: restart # 如果 strategy 是 restart_context ,这个端点需要开启

这里配置文件的名称和命名空间通过环境变量动态传递进来。

启动类增加 @EnableDiscoveryClient@EnableFeignClients 注解:

@EnableFeignClients
@EnableDiscoveryClient
@SpringBootApplication
public class ConsumerServiceApplication {public static void main(String[] args) {SpringApplication.run(ConsumerServiceApplication.class, args);}}

使用 FeignClient 声明服务提供者的接口:

@Component
@FeignClient(value = "provider")
public interface ProviderClient {@GetMapping("/rpc")String rpc();}

创建动态配置类:

@Component
@ConfigurationProperties(prefix = "param")
@Data
@RefreshScope
public class ParamProperties {private String text;private String name;
}

创建测试接口,分别测试 discoveryrpc 调用、动态配置:

@RestController
@RefreshScope
public class ConsumerController {@ResourceProviderClient providerClient;@Value("${param.param1:tmp}")private String param;@Resourceprivate DiscoveryClient discoveryClient;@ResourceParamProperties paramProperties;/*** 查看 Discovery 信息*/@GetMapping("/discoveryInfo")public Map<String, List<ServiceInstance>> getDiscoveryInfo() {Map<String, List<ServiceInstance>> result = new HashMap<>();discoveryClient.getServices().forEach(service -> {List<ServiceInstance> instances = discoveryClient.getInstances(service);result.put(service,instances);});return result;}/*** 测试RPC调用*/@GetMapping("/rpc")public String rpc() {String providerData = providerClient.rpc();return "sunncess: consumer from : " + getCurrentHostName() + " ; " + providerData;}/*** 测试动态配置*/@GetMapping("/config")public String getConfig() {return "success: current config : " + param+": "+paramProperties.toString();}/*** 获取当前系统名称*/private String getCurrentHostName() {try {InetAddress inetAddress = InetAddress.getLocalHost();return inetAddress.getHostName();} catch (UnknownHostException e) {e.printStackTrace();}return null;}}

建立 Dockerfile 文件,内容如下:

FROM java:8
MAINTAINER bxc
WORKDIR /app
ADD target/consumer-service-0.0.1-SNAPSHOT.jar /app/app.jar
CMD ["java", "-jar", "app.jar"]

四、部署到 K8s

4.1 打包上传镜像

这里我将镜像打包后上传至私有的 harbor 仓库中,如果没有 harbor 仓库也可以将镜像上传至每个 k8s node 节点中,然后使用 docker load 到本地镜像仓库中。

# 打包成 jar 包
mvn clean package
# 构建镜像
cd consumer-service
docker build -t consumer-service:1.0 .
# 上传至 harbor
docker tag consumer-service:1.0 11.0.1.150/image/consumer-service:1.0 .
docker push 11.0.1.150/image/consumer-service:1.0# 构建镜像
cd ../provider-service
docker build -t provider-service:1.0 .
# 上传至 harbor
docker tag provider-service:1.0 11.0.1.150/image/provider-service:1.0 .
docker push 11.0.1.150/image/provider-service:1.0

4.2 k8s 部署服务

首先创建 cloud 命名空间,后续所有服务都建立在该命名空间下:

kubectl create ns cloud

由于默认的 ServiceAccount 是没有权限访问K8SAPI Server资源的,直接启动服务读取 ConfigMap 会报错 Forbidden!Configured service account doesn't have access. ,所以在启动服务前,先创建一个分配权限的 ServiceAccount,但是需要注意:由于指定了 ServiceAccount,该 ServiceAccount 同时也需要给 Services 的权限,不然 discoveryInfo 读取不到 Service 的信息:

vi role.yml
apiVersion: v1
kind: ServiceAccount
metadata:name: config-readernamespace: cloud
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:namespace: cloudname: pod-reader
rules:- apiGroups: [""]resources: ["pods","configmaps","secrets","endpoints","services"]verbs: ["get", "watch", "list"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:name: config-reader-rolebindingnamespace: cloud
roleRef:apiGroup: rbac.authorization.k8s.iokind: Rolename: pod-readernamespace: cloud
subjects:- kind: ServiceAccountname: config-readernamespace: cloud
kubectl apply -f role.yml

查看:
在这里插入图片描述

下面部署服务,注意:服务需要指定上面创建的 ServiceAccount ,不然没有权限获取 ConfigMap 配置以及 Service 信息。

vi cloud.yml
apiVersion: v1
kind: Service
metadata:name: consumernamespace: cloudlabels:app: consumer
spec:type: NodePortports:- port: 8080name: clientnodePort: 31880targetPort: 8080selector:app: consumer---
kind: ConfigMap
apiVersion: v1
metadata:name: consumerconfignamespace: cloud
data:application.yml: |-param:param1: abctext: textabcname: nameabc---
apiVersion: apps/v1
kind: Deployment
metadata:name: consumernamespace: cloud
spec:replicas: 2selector:matchLabels:app: consumertemplate:metadata:labels:app: consumerspec:serviceAccountName: config-readercontainers:- name: consumerimage: consumer-service:1.0ports:- containerPort: 8080name: serverenv:- name: CONFIG_NAMEvalue: consumerconfig- name: NAMESPACEvalue: cloud---
apiVersion: v1
kind: Service
metadata:name: providernamespace: cloudlabels:app: provider-svc
spec:type: ClusterIPports:- port: 8091targetPort: 8091selector:app: provider---
apiVersion: apps/v1
kind: Deployment
metadata:name: providernamespace: cloud
spec:replicas: 2selector:matchLabels:app: providertemplate:metadata:labels:app: providerspec:serviceAccountName: config-readercontainers:- name: providerimage: provider-service:1.0ports:- containerPort: 8091name: server

注意这里配置信息 ConfigMap 的名字为 consumerconfig ,需要传递到服务消费者服务中,并且记住此时的配置内容:

在这里插入图片描述

启动服务:

kubectl apply -f cloud.yml

查看 Pod

kubectl get pods -n cloud

在这里插入图片描述

都处于 Running 状态,查看其中一个的启动日志,看是否正常启动:

kubectl logs consumer-77c674cc96-l58jv -n cloud

在这里插入图片描述

服务启动正常。

五、测试

5.1 服务注册发现测试

调用 http:{node ip}:31880/discoveryInfo 查看服务信息:

在这里插入图片描述

结果完整信息如下:

{"provider": [{"instanceId": "47d501db-646c-401a-9d84-63ef5909cd10","serviceId": "provider","host": "10.233.111.243","port": 8091,"uri": "http://10.233.111.243:8091","secure": false,"metadata": {"app": "provider-svc","kubectl.kubernetes.io/last-applied-configuration": "{\"apiVersion\":\"v1\",\"kind\":\"Service\",\"metadata\":{\"annotations\":{},\"labels\":{\"app\":\"provider-svc\"},\"name\":\"provider\",\"namespace\":\"cloud\"},\"spec\":{\"ports\":[{\"port\":8091,\"targetPort\":8091}],\"selector\":{\"app\":\"provider\"},\"type\":\"ClusterIP\"}}\n"},"namespace": null,"scheme": "http"},{"instanceId": "493dad3f-76b4-46a2-b882-3607ac3751f4","serviceId": "provider","host": "10.233.111.244","port": 8091,"uri": "http://10.233.111.244:8091","secure": false,"metadata": {"app": "provider-svc","kubectl.kubernetes.io/last-applied-configuration": "{\"apiVersion\":\"v1\",\"kind\":\"Service\",\"metadata\":{\"annotations\":{},\"labels\":{\"app\":\"provider-svc\"},\"name\":\"provider\",\"namespace\":\"cloud\"},\"spec\":{\"ports\":[{\"port\":8091,\"targetPort\":8091}],\"selector\":{\"app\":\"provider\"},\"type\":\"ClusterIP\"}}\n"},"namespace": null,"scheme": "http"}],"consumer": [{"instanceId": "a21109cd-ca86-4b0d-a9ca-b8c0efa336c8","serviceId": "consumer","host": "10.233.111.245","port": 8080,"uri": "http://10.233.111.245:8080","secure": false,"metadata": {"app": "consumer","kubectl.kubernetes.io/last-applied-configuration": "{\"apiVersion\":\"v1\",\"kind\":\"Service\",\"metadata\":{\"annotations\":{},\"labels\":{\"app\":\"consumer\"},\"name\":\"consumer\",\"namespace\":\"cloud\"},\"spec\":{\"ports\":[{\"name\":\"client\",\"nodePort\":31880,\"port\":8080,\"targetPort\":8080}],\"selector\":{\"app\":\"consumer\"},\"type\":\"NodePort\"}}\n","port.client": "8080"},"namespace": null,"scheme": "http"},{"instanceId": "591e43a8-6d30-4395-915e-570275ee0336","serviceId": "consumer","host": "10.233.111.246","port": 8080,"uri": "http://10.233.111.246:8080","secure": false,"metadata": {"app": "consumer","kubectl.kubernetes.io/last-applied-configuration": "{\"apiVersion\":\"v1\",\"kind\":\"Service\",\"metadata\":{\"annotations\":{},\"labels\":{\"app\":\"consumer\"},\"name\":\"consumer\",\"namespace\":\"cloud\"},\"spec\":{\"ports\":[{\"name\":\"client\",\"nodePort\":31880,\"port\":8080,\"targetPort\":8080}],\"selector\":{\"app\":\"consumer\"},\"type\":\"NodePort\"}}\n","port.client": "8080"},"namespace": null,"scheme": "http"}]
}

可以看到服务提供者和服务消费的基本信息都获取到了,其中的 ip 地址就是 pod 的地址:

kubectl get pods -n cloud -o wide

在这里插入图片描述

调用 http:{node ip}:31880/rpc 测试 RPC 远程调用,并且多次调用:

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

经过多次调用可以看到负载均衡效果。

5.2 服务动态配置测试

调用 http:{node ip}:31880/config 查看配置信息:

在这里插入图片描述

正常读取到 ConfigMap 中的配置信息,下面修改 ConfigMap 信息:

kubectl edit configmap consumerconfig -n cloud

修改信息如下:

在这里插入图片描述
然后保存后再次调用查看配置接口:

在这里插入图片描述

配置已生效。

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

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

相关文章

MFC 列表控件修改实例(源码下载)

1、本程序基于前期我的博客文章《MFC下拉菜单打钩图标存取实例&#xff08;源码下载&#xff09;》 2、程序功能选中列表控件某一项&#xff0c;修改这一项的按钮由禁止变为可用&#xff0c;双击这个按钮弹出对话框可对这一项的记录数据进行修改&#xff0c;点击确定保存修改数…

基于Spring Boot的体质测试数据分析及可视化系统设计与实现

基于Spring Boot的体质测试数据分析及可视化系统的设计与实现 开发语言&#xff1a;Java框架&#xff1a;springbootJDK版本&#xff1a;JDK1.8数据库工具&#xff1a;Navicat11开发软件&#xff1a;eclipse/myeclipse/idea 系统部分展示 前台首页界面图&#xff0c;体质测试…

Github查找代码项目高级语法(含科研项目查找案例)

基础搜索语法 1.搜索名字 in:name XXX 2.搜索描述 in:description XXX 3.搜索readme in:readme XXX 4.根据stars stars:>2000 5.根据fork fork:>3000 6.仓库大小搜索 size:>5000 [注意: 该处单位大小为 k] 7.根据更新时间 …

免费开源语音克隆-GPT-SoVITS-WebUI只需 5 秒的声音样本

语音克隆-GPT-SoVITS-WebUI 强大的少样本语音转换与语音合成Web用户界面。 功能&#xff1a; 零样本文本到语音&#xff08;TTS&#xff09;&#xff1a; 输入 5 秒的声音样本&#xff0c;即刻体验文本到语音转换。 少样本 TTS&#xff1a; 仅需 1 分钟的训练数据即可微调模型…

OpenCV如何在图像中寻找轮廓(60)

返回:OpenCV系列文章目录&#xff08;持续更新中......&#xff09; 上一篇&#xff1a;OpenCV如何模板匹配(59) 下一篇 :OpenCV检测凸包(61) 目标 在本教程中&#xff0c;您将学习如何&#xff1a; 使用 OpenCV 函数 cv::findContours使用 OpenCV 函数 cv::d rawContours …

【Flask 系统教程 4】Jinjia2模版和语法

Jinjia2 模板 模板的介绍 Jinja2 是一种现代的、设计优雅的模板引擎&#xff0c;它是 Python 的一部分&#xff0c;由 Armin Ronacher 开发。Jinja2 允许你在 HTML 文档中嵌入 Python 代码&#xff0c;以及使用变量、控制结构和过滤器来动态生成内容。它的语法简洁清晰&#…

【Qt QML】用CMake管理Qt工程

CMake是一个开源、跨平台的工具系列&#xff0c;用于构建、测试和打包软件。CMake使用简单的独立配置文件来控制软件编译过程。与许多跨平台系统不同&#xff0c;CMake被设计为与本地构建环境结合使用。 下面我们在CMake项目中使用Qt的最基本方法。首先&#xff0c;创建一个基本…

系统架构设计师错题集

在实时操作系统中&#xff0c;两个任务并发执行&#xff0c;一个任务要等待另一个任务发来消息&#xff0c;或建立某个条件后再向前执行&#xff0c;这种制约性合作关系被称为任务的&#xff08;9&#xff09;。 (9)A.同步 B.互斥 C.调度 D.执行 【答案】A 【解析】本题考查…

Linux图形化界面怎么进入?CentOS 7图形界面切换

CentOS 7默认只安装命令行界面。要切换到图形界面&#xff0c;需要先检查系统是否安装图形界面&#xff0c;在终端输入以下命令&#xff1a; systemctl get-default若是返回结果是“multi-user.target”表示系统没有安装图形界面&#xff1b;若是返回结果是“graphical.target…

Linux migrate_type进一步探索

文章接着上回Linux migrate_type初步探索 1、物理页面添加到buddy系统 我们都知道物理内存一开始是由memblock进行分配管理&#xff0c;后面会切换到buddy系统管理。那么接下来我们看一下&#xff0c;memblock管理的物理页面是怎么添加到buddy系统中的。 start_kernel() -&g…

双指针(C++)

文章目录 1、移动零2、复写零3、快乐数4、盛最多水的容器5、有效三角形的个数6、和为s的两个数7、三数之和8、四数之和 需要理解的是&#xff0c;双指针并非只有指针&#xff0c;双指针的意思是两个位置。比如对于数组来说&#xff0c;两个下标也是双指针。当然&#xff0c;也可…

merge and rebase

文章目录 什么是merge什么是rebasemerge和rebase的区别操作执行git merge操作git rebase操作冲突解决解决冲突的步骤 Git Merge 和 Git Rebase 都是用于集成来自不同分支的修改的 Git 命令。 什么是merge Git Merge 是将一个分支的改动合并到另一个分支的方式。当你执行一个 m…

LabVIEW机械臂控制与图像处理示教平台

LabVIEW机械臂控制与图像处理示教平台 随着工业自动化技术的快速发展&#xff0c;工业机器人在制造业中的应用越来越广泛&#xff0c;它们在提高生产效率、降低人工成本以及保证产品质量方面发挥着重要作用。然而&#xff0c;传统的工业机器人编程和操作需要专业知识&#xff…

2024-5-3学习笔记 虚拟继承原理

目录 原理 总结 前面提到过&#xff0c;解决菱形继承产生的数据二义性问题和数据冗余&#xff0c;就需要用到虚拟继承&#xff0c;关于它是如何解决的&#xff0c;我们来一起研究。 class Person { public :string _name ; // 姓名 }; class Student : virtual public Perso…

某招聘网站搜索结果接口逆向之webpack扣取

逆向网址 aHR0cHM6Ly93ZS41MWpvYi5jb20v 逆向链接 aHR0cHM6Ly93ZS41MWpvYi5jb20vcGMvc2VhcmNoP2pvYkFyZWE9MDAwMDAwJmtleXdvcmQ9cGhwJnNlYXJjaFR5cGU9MiZrZXl3b3JkVHlwZT0 逆向接口 aHR0cHM6Ly93ZS41MWpvYi5jb20vYXBpL2pvYi9zZWFyY2gtcGM 逆向过程 请求方式 POST 逆向参数 …

华为Pura70发布,供应链公司进入静默保密期

保密措施&#xff1a;与华为Pura70发布相关的供应链公司在产品发布前后处于静默保密期。这可能是由于华为对于手机供应链的一些信息处于保密状态&#xff0c;尤其是关于麒麟芯片的代工厂商等敏感信息。这种保密措施有助于保持产品的神秘感&#xff0c;调动用户的好奇心&#xf…

微信小程序+esp8266温湿度读取

本文主要使用微信小程序显示ESP8266读取的温湿度并通过微信小程序控制LED灯。小程序界面如下图所示 原理讲解 esp8266 通过mqtt发布消息,微信小程序通过mqtt 订阅消息,小程序订阅后,就可以实时收到esp8266 传输来的消息。 个人可免费注册五个微信小程序账号,在微信小程序官…

开源框架 NanUI 项目宣布将暂停开发,作者转行卖钢材

NanUI 界面组件是一个开源的 .NET 窗体应用程序界面框架&#xff0c;适用于希望使用 HTML5 / CSS3 等前端技术来构建 Windows 窗体应用程序用户界面的 .NET 开发人员。 该项目的作者林选臣日前在 GitHub 上发布了停更公告&#xff0c;称因去年被裁员失业&#xff0c;他目前已经…

Redis 实战1

SDS Redis 只会使用 C 字符串作为字面量&#xff0c; 在大多数情况下&#xff0c; Redis 使用 SDS &#xff08;Simple Dynamic String&#xff0c;简单动态字符串&#xff09;作为字符串表示。 比起 C 字符串&#xff0c; SDS 具有以下优点&#xff1a; 常数复杂度获取字符串…

R语言学习—4—数据矩阵及R表示

1、创建向量、矩阵 在R中&#xff0c;c()函数用于创建向量或组合数据对象。它在某些情况下可能会被省略&#xff0c;因为R有一些隐式的向量创建规则。例如&#xff0c;当你使用:操作符创建一个数字序列时&#xff0c;R会自动创建一个向量&#xff0c;所以你不需要显式地调用c()…