DHorse(K8S的CICD平台)的实现原理

综述

首先,本篇文章所介绍的内容,已经有完整的实现,可以参考这里。
在微服务、DevOps和云平台流行的当下,使用一个高效的持续集成工具也是一个非常重要的事情。虽然市面上目前已经存在了比较成熟的自动化构建工具,比如jekines,还有一些商业公司推出的自动化构建工具,但他们都不能够很好的和云环境相结合。那么究竟该如何实现一个简单、快速的基于云环境的自动化构建系统呢?我们首先以一个Springboot应用为例来介绍一下整体的发布流程,然后再来看看具体如何实现。发布的步骤大体如下:
1.首先从代码仓库下载代码,比如Gitlab、GitHub等;
2.接着是进行打包,比如使用Maven、Gradle等;
3.如果要使用k8s作为编排,还需要把步骤2产生的包制作成镜像,比如用Docker等;
4.上传步骤3的镜像到远程仓库,比如Harhor、DockerHub等;
5.最后,下载镜像并编写Deployment文件部署到k8s集群;
如图1所示:
在这里插入图片描述
图1

从以上步骤可以看出,发布过程中需要的工具和环境至少包括:代码仓库(Gitlab、GitHub等)、打包环境(Maven、Gradle等)、镜像制作(Docker等)、镜像仓库(Harbor、DockerHub等)、k8s集群等;此外,还包括发布系统自身的数据存储等。
可以看出,整个流程里依赖的环境很多,如果发布系统不能与这些环境解耦,那么要想实现一个安装简单、功能快速的系统没有那么容易。那么有没有合理的解决方案来实现与这些环境的解耦呢?答案是有的,下面就分别介绍。

代码仓库

操作代码仓库,一般系统提供的都有对应Restful API,以GitLab系统提供的Java客户端为例,如下代码:

<dependency>
	<groupId>org.gitlab4j</groupId>
	<artifactId>gitlab4j-api</artifactId>
	<version>4.17.0</version>
</dependency>

比如,我们想获取某个项目的分支列表,如下代码所示:

public List<Branch> branchList(CodeRepo codeRepo, BranchListParam param) {
	GitLabApi gitLabApi = gitLabApi(codeRepo);
	List<Branch> list = null;
	try {
		list = gitLabApi.getRepositoryApi().getBranches(param.getProjectIdOrPath(), param.getBranchName());
	} catch (GitLabApiException e) {
		LogUtils.throwException(logger, e, MessageCodeEnum.PROJECT_BRANCH_PAGE_FAILURE);
	} finally {
		gitLabApi.close();
	}
}

private GitLabApi gitLabApi(CodeRepo codeRepo) {
	GitLabApi gitLabApi = new GitLabApi(codeRepo.getUrl(), codeRepo.getAuthToken());
	gitLabApi.setRequestTimeout(1000, 5 * 1000);
	try {
		gitLabApi.getVersion();
	}catch(GitLabApiException e) {
		//如果token无效,则用账号登录
		if(e.getHttpStatus() == 401 && !StringUtils.isBlank(codeRepo.getAuthUser())) {
			gitLabApi = new GitLabApi(codeRepo.getUrl(), codeRepo.getAuthUser(), codeRepo.getAuthPassword());
			gitLabApi.setRequestTimeout(1000, 5 * 1000);
		}
	}
	
	return gitLabApi;
}

打包环境

我们以Maven为例进行说明,一般情况下,我们使用Maven打包时,需要首先安装Maven环境,接着引入打包插件,然后使用mvn clean package命令就可以打包了。比如springboot自带插件:

<plugin>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-maven-plugin</artifactId>
	<version>2.5.6</version>
	<configuration>
		<classifier>execute</classifier>
		<mainClass>com.test.Application</mainClass>
	</configuration>
	<executions>
		<execution>
			<goals>
				<goal>repackage</goal>
			</goals>
		</execution>
	</executions>
</plugin>

再比如,通用的打包插件:

<plugin>
	<groupId>org.apache.maven.plugins</groupId>
	<artifactId>maven-assembly-plugin</artifactId>
	<version>3.8.2</version>
	<configuration>
		<appendAssemblyId>false</appendAssemblyId>
		<descriptors>
			<descriptor>src/main/resources/assemble.xml</descriptor>
		</descriptors>
		<outputDirectory>../target</outputDirectory>
	</configuration>
	<executions>
		<execution>
			<id>make-assembly</id>
			<phase>package</phase>
			<goals>
				<goal>single</goal>
			</goals>
		</execution>
	</executions>
</plugin>

等等。然后再通过运行mvn clean package命令进行打包。那么,在打包时如果要去除对maven环境的依赖,该如何实现呢?
可以使用嵌入式maven插件maven-embedder来实现。
具体可以这样来做,首先在平台项目里引入依赖,如下:

<dependency>
	<groupId>org.apache.maven</groupId>
	<artifactId>maven-embedder</artifactId>
	<version>3.8.1</version>
</dependency>
<dependency>
	<groupId>org.apache.maven</groupId>
	<artifactId>maven-compat</artifactId>
	<version>3.8.1</version>
</dependency>
<dependency>
	<groupId>org.apache.maven.resolver</groupId>
	<artifactId>maven-resolver-connector-basic</artifactId>
	<version>1.7.1</version>
</dependency>
<dependency>
	<groupId>org.apache.maven.resolver</groupId>
	<artifactId>maven-resolver-transport-http</artifactId>
	<version>1.7.1</version>
</dependency>

运行如下代码,就可以对项目进行打包了:

String[] commands = new String[] { "clean", "package", "-Dmaven.test.skip" };
String pomPath = "D:/hello/pom.xml";
MavenCli cli = new MavenCli();
try {
	cli.doMain(commands, pomPath, System.out, System.out);
} catch (Exception e) {
	e.printStackTrace();
}

但是,一般情况下,我们通过maven的settings文件还会做一些配置,比如配置工作目录、nexus私服地址、Jdk版本、编码方式等等,如下:

<?xml version="1.0" encoding="UTF-8"?>
<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0 http://maven.apache.org/xsd/settings-1.0.0.xsd">
	<localRepository>C:/m2/repository</localRepository>
	<profiles>
		<profile>
			<id>myNexus</id>
			<repositories>
				<repository>
					<id>nexus</id>
					<name>nexus</name>
					<url>https://repo.maven.apache.org/maven2</url>
					<releases>
						<enabled>true</enabled>
					</releases>
					<snapshots>
						<enabled>true</enabled>
					</snapshots>
				</repository>
			</repositories>
			<pluginRepositories>
				<pluginRepository>
					<id>nexus</id>
					<name>nexus</name>
					<url>https://repo.maven.apache.org/maven2</url>
					<releases>
						<enabled>true</enabled>
					</releases>
					<snapshots>
						<enabled>true</enabled>
					</snapshots>
				</pluginRepository>
			</pluginRepositories>
		</profile>

		<profile>
			<id>java11</id>
			<activation>
				<activeByDefault>true</activeByDefault>
				<jdk>11</jdk>
			</activation>
			<properties>
				<maven.compiler.source>11</maven.compiler.source>
				<maven.compiler.target>11</maven.compiler.target>
				<maven.compiler.compilerVersion>11</maven.compiler.compilerVersion>
				<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
				<project.build.outputEncoding>UTF-8</project.build.outputEncoding>
			</properties>
		</profile>
	</profiles>
	<activeProfiles>
		<activeProfile>myNexus</activeProfile>
	</activeProfiles>
</settings>

通过查看MavenCli类发现,doMain(CliRequest cliRequest)方法有比较丰富的参数,CliRequest的代码如下:

package org.apache.maven.cli;

public class CliRequest
{
    String[] args;

    CommandLine commandLine;

    ClassWorld classWorld;

    String workingDirectory;

    File multiModuleProjectDirectory;

    boolean debug;

    boolean quiet;

    boolean showErrors = true;

    Properties userProperties = new Properties();

    Properties systemProperties = new Properties();

    MavenExecutionRequest request;

    CliRequest( String[] args, ClassWorld classWorld )
    {
        this.args = args;
        this.classWorld = classWorld;
        this.request = new DefaultMavenExecutionRequest();
    }

    public String[] getArgs()
    {
        return args;
    }

    public CommandLine getCommandLine()
    {
        return commandLine;
    }

    public ClassWorld getClassWorld()
    {
        return classWorld;
    }

    public String getWorkingDirectory()
    {
        return workingDirectory;
    }

    public File getMultiModuleProjectDirectory()
    {
        return multiModuleProjectDirectory;
    }

    public boolean isDebug()
    {
        return debug;
    }

    public boolean isQuiet()
    {
        return quiet;
    }

    public boolean isShowErrors()
    {
        return showErrors;
    }

    public Properties getUserProperties()
    {
        return userProperties;
    }

    public Properties getSystemProperties()
    {
        return systemProperties;
    }

    public MavenExecutionRequest getRequest()
    {
        return request;
    }

    public void setUserProperties( Properties properties ) 
    {
        this.userProperties.putAll( properties );      
    }
}

可以看出,这些参数非常丰富,也许可以满足我们的需求,但是CliRequest只有一个默认修饰符的构造方法,也就说只有位于org.apache.maven.cli包下的类才有访问CliRequest构造方法的权限,我们可以在平台项目里新建一个包org.apache.maven.cli,然后再创建一个类(如:DefaultCliRequest)继承自CliRequest,然后实现一个public的构造方法,就可以在任何包里使用该类了,如下代码:

package org.apache.maven.cli;

import org.codehaus.plexus.classworlds.ClassWorld;

public class DefaultCliRequest extends CliRequest{

	public DefaultCliRequest(String[] args, ClassWorld classWorld) {
		super(args, classWorld);
	}
	
	public void setWorkingDirectory(String directory) {
		this.workingDirectory = directory;
	}
}

定义好参数类型DefaultCliRequest后,我们再来看看打包的代码:

public void doPackage() {
	String[] commands = new String[] { "clean", "package", "-Dmaven.test.skip" };
	DefaultCliRequest request = new DefaultCliRequest(commands, null);
	request.setWorkingDirectory("D:/hello/pom.xml");

	Repository repository = new Repository();
	repository.setId("nexus");
	repository.setName("nexus");
	repository.setUrl("https://repo.maven.apache.org/maven2");
	RepositoryPolicy policy = new RepositoryPolicy();
	policy.setEnabled(true);
	policy.setUpdatePolicy("always");
	policy.setChecksumPolicy("fail");
	repository.setReleases(policy);
	repository.setSnapshots(policy);

	String javaVesion = "11";
	Profile profile = new Profile();
	profile.setId("java11");
	Activation activation = new Activation();
	activation.setActiveByDefault(true);
	activation.setJdk(javaVesion);
	profile.setActivation(activation);
	profile.setRepositories(Arrays.asList(repository));
	profile.setPluginRepositories(Arrays.asList(repository));

	Properties properties = new Properties();
	properties.put("java.home", "D:/java/jdk-11.0.16.2");
	properties.put("java.version", javaVesion);
	properties.put("maven.compiler.source", javaVesion);
	properties.put("maven.compiler.target", javaVesion);
	properties.put("maven.compiler.compilerVersion", javaVesion);
	properties.put("project.build.sourceEncoding", "UTF-8");
	properties.put("project.reporting.outputEncoding", "UTF-8");
	profile.setProperties(properties);
	MavenExecutionRequest executionRequest = request.getRequest();
	executionRequest.setProfiles(Arrays.asList(profile));

	MavenCli cli = new MavenCli();
	try {
		cli.doMain(request);
	} catch (Exception e) {
		e.printStackTrace();
	}
}

如果需要设置其他参数,也可以通过以上参数自行添加。

镜像制作

一般情况下,我们在Docker环境中通过Docker命令来制作镜像,过程如下:
1.首先编写Dockerfile文件;
2.通过docker build制作镜像;
3.通过docker push上传镜像;
可以看出,如果要使用docker制作镜像的话,必须要有docker环境,而且需要编写Dockerfile文件。当然,也可以不用安装docker环境,直接使用doker的远程接口:post/build。但是,在远程服务器中仍然需要安装doker环境和编写Dockerfile。在不依赖Docker环境的情况下,仍然可以制作镜像,下面就介绍一款工具Jib的用法。
Jib是谷歌开源的一套工具,github地址,它是一个无需Docker守护进程——也无需深入掌握Docker最佳实践的情况下,为Java应用程序构建Docker和OCI镜像, 它可以作为Maven和Gradle的插件,也可以作为Java库。

比如,使用jib-maven-plugin插件构建镜像的代码如下:

<plugin>
	<groupId>com.google.cloud.tools</groupId>
	<artifactId>jib-maven-plugin</artifactId>
	<version>3.3.0</version>
	<configuration>
		<from>
			<image>openjdk:13-jdk-alpine</image>
		</from>
		<to>
			<image>gcr.io/dhorse/client</image>
			<tags>
				<tag>102</tag>
			</tags>
			<auth>
				<!--连接镜像仓库的账号和密码 -->
				<username>username</username>
				<password>password</password>
			</auth>
		</to>
		<container>
			<ports>
				<port>8080</port>
			</ports>
		</container>
	</configuration>
	<executions>
		<execution>
			<phase>package</phase>
			<goals>
				<goal>build</goal>
			</goals>
		</execution>
	</executions>
</plugin>

然后使用命令进行构建:

mvn compile jib:build

可以看出,无需docker环境就可以实现镜像的构建。但是,要想通过平台类型的系统去为每个系统构建镜像,显然通过插件的方式,不太合适,因为需要每个被构建系统引入jib-maven-plugin插件才行,也就是需要改造每一个系统,这样就会带来一定的麻烦。那么有没有不需要改造系统的方式直接进行构建镜像呢?答案是通过Jib-core就可以实现。

首先,在使用Jib-core的项目中引入依赖,maven如下:

<dependency>
	<groupId>com.google.cloud.tools</groupId>
	<artifactId>jib-core</artifactId>
	<version>0.22.0</version>
</dependency>

然后就可以直接使用Jib-core的API来进行制作镜像,如下代码:

try {
	JibContainerBuilder jibContainerBuilder = null;
	if (StringUtils.isBlank(context.getProject().getBaseImage())) {
		jibContainerBuilder = Jib.fromScratch();
	} else {
		jibContainerBuilder = Jib.from(context.getProject().getBaseImage());
	}
	//连接镜像仓库5秒超时
	System.setProperty("jib.httpTimeout", "5000");
	System.setProperty("sendCredentialsOverHttp", "true");
	String fileNameWithExtension = targetFiles.get(0).toFile().getName();
	List<String> entrypoint = Arrays.asList("java", "-jar", fileNameWithExtension);
	RegistryImage registryImage = RegistryImage.named(context.getFullNameOfImage()).addCredential(
			context.getGlobalConfigAgg().getImageRepo().getAuthUser(),
			context.getGlobalConfigAgg().getImageRepo().getAuthPassword());
	jibContainerBuilder.addLayer(targetFiles, "/")
		.setEntrypoint(entrypoint)
		.addVolume(AbsoluteUnixPath.fromPath(Paths.get("/etc/localtime")))
		.containerize(Containerizer.to(registryImage)
				.setAllowInsecureRegistries(true)
				.addEventHandler(LogEvent.class, logEvent -> logger.info(logEvent.getMessage())));
} catch (Exception e) {
	logger.error("Failed to build image", e);
	return false;
}

其中,targetFiles是要构建镜像的目标文件,比如springboot打包后的jar文件。
通过Jib-core,可以很轻松的实现镜像构建,而不需要依赖任何其他环境,也不需要被构建系统做任何改造,非常方便。

镜像仓库

类似代码仓库提供的Restful API,也可以通过Restful API来操作镜像仓库,以Harbor创建一个项目为例,代码如下:

public void createProject(ImageRepo imageRepo) {
	String uri = "api/v2.0/projects";
	if(!imageRepo.getUrl().endsWith("/")) {
		uri = "/" + uri;
	}
	HttpPost httpPost = new HttpPost(imageRepo.getUrl() + uri);
	RequestConfig requestConfig = RequestConfig.custom()
			.setConnectionRequestTimeout(5000)
			.setConnectTimeout(5000)
			.setSocketTimeout(5000)
			.build();
	httpPost.setConfig(requestConfig);
	httpPost.setHeader("Content-Type", "application/json;charset=UTF-8");
	httpPost.setHeader("Authorization", "Basic "+ Base64.getUrlEncoder().encodeToString((imageRepo.getAuthUser() + ":" + imageRepo.getAuthPassword()).getBytes()));
	ObjectNode objectNode = JsonUtils.getObjectMapper().createObjectNode();
	objectNode.put("project_name", "dhorse");
	//1:公有类型
	objectNode.put("public", 1);
	httpPost.setEntity(new StringEntity(objectNode.toString(),"UTF-8"));
	try (CloseableHttpResponse response = createHttpClient(imageRepo.getUrl()).execute(httpPost)){
		if (response.getStatusLine().getStatusCode() != 201
				&& response.getStatusLine().getStatusCode() != 409) {
			LogUtils.throwException(logger, response.getStatusLine().getReasonPhrase(),
					MessageCodeEnum.IMAGE_REPO_PROJECT_FAILURE);
		}
	} catch (IOException e) {
		LogUtils.throwException(logger, e, MessageCodeEnum.IMAGE_REPO_PROJECT_FAILURE);
	}
}

k8s集群

同样,k8s也提供了Restful API。同时,官方也提供了各种语言的客户端,下面以Java语言的客户端为例,来创建一个deployment。
首先,引入Maven依赖:

<dependency>
	<groupId>io.kubernetes</groupId>
	<artifactId>client-java</artifactId>
	<version>13.0.0</version>
</dependency>

然后,使用如下代码:

public boolean createDeployment(DeployContext context) {
	V1Deployment deployment = new V1Deployment();
	deployment.apiVersion("apps/v1");
	deployment.setKind("Deployment");
	deployment.setMetadata(deploymentMetaData(context.getDeploymentAppName()));
	deployment.setSpec(deploymentSpec(context));
	ApiClient apiClient = this.apiClient(context.getCluster().getClusterUrl(),
			context.getCluster().getAuthToken(), 1000, 1000);
	AppsV1Api api = new AppsV1Api(apiClient);
	CoreV1Api coreApi = new CoreV1Api(apiClient);
	String namespace = context.getProjectEnv().getNamespaceName();
	String labelSelector = K8sUtils.getDeploymentLabelSelector(context.getDeploymentAppName());
	try {
		V1DeploymentList oldDeployment = api.listNamespacedDeployment(namespace, null, null, null, null,
				labelSelector, null, null, null, null, null);
		if (CollectionUtils.isEmpty(oldDeployment.getItems())) {
			deployment = api.createNamespacedDeployment(namespace, deployment, null, null, null);
		} else {
			deployment = api.replaceNamespacedDeployment(context.getDeploymentAppName(), namespace, deployment, null, null,
					null);
		}
	} catch (ApiException e) {
		if (!StringUtils.isBlank(e.getMessage())) {
			logger.error("Failed to create k8s deployment, message: {}", e.getMessage());
		} else {
			logger.error("Failed to create k8s deployment, message: {}", e.getResponseBody());
		}
		return false;
	}
	return true;
}

private ApiClient apiClient(String basePath, String accessToken, int connectTimeout, int readTimeout) {
	ApiClient apiClient = new ClientBuilder().setBasePath(basePath).setVerifyingSsl(false)
			.setAuthentication(new AccessTokenAuthentication(accessToken)).build();
	apiClient.setConnectTimeout(connectTimeout);
	apiClient.setReadTimeout(readTimeout);
	return apiClient;
}

至此,关键的技术点已经介绍完了,更多内容,请参考这里

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

2.前端调试(控制台使用)

消息堆叠 如果一条消息连续重复&#xff0c;而不是在新行上输出每一个消息实例&#xff0c;控制台将“堆叠”消息并在左侧外边距显示一个数字。此数字表示该消息已重复的次数。 如果您倾向于为每一个日志使用一个独特的行条目&#xff0c;请在 DevTools 设置中启用 Show times…

银行余额修改生成器,虚拟农业建设工商邮政中国,画板+取快照生成png高清图

在网上找了很多模版&#xff0c;一共好几个&#xff0c;然后都插入到了图片资源库里面&#xff0c;点击指定的单选框就会自动更换易语言画板上面的图片&#xff0c;然后模版上面都对应了指定的标签【透明状态覆盖了原有的字符】&#xff0c;然后在指定的参数上面对应加入了指定…

小甲鱼python零基础入门学习(一)

目录 一、环境搭建和课程介绍 &#xff08;1&#xff09;安装最新版本的python3.x &#xff08;2&#xff09;安装编辑器&#xff08;找合适自己的&#xff09; 二、用python设计第一个游戏 三、变量和字符串 &#xff08;1&#xff09;变量 &#xff08;2&#xff09;字…

PTA_乙级_1096

Q1&#xff1a;因数 在数学中&#xff0c;一个数的因数是能够整除该数的整数。换句话说&#xff0c;如果我们将一个数 a 除以另一个整数 b 而得到整数商&#xff0c;那么 b 就是 a 的因数。以下是一些例子&#xff1a; 1.因数的定义&#xff1a; 如果整数 b 可以被整数 a 整除&…

海康工业相机如何提高相机帧率

影响帧率的因素 相机参数 帧率限制使能 像素格式 曝光时间 数据包大小&#xff08;网口&#xff09; 相机默认参数 ADC位深 系统环境设置

浅谈泛在电力物联网在智能配电系统应用

贾丽丽 安科瑞电气股份有限公司 上海嘉定 201801 摘要&#xff1a;在社会经济和科学技术不断发展中&#xff0c;配电网实现了角色转变&#xff0c;传统的单向供电服务形式已经被双向能流服务形式取代&#xff0c;社会多样化的用电需求也得以有效满足。随着物联网技术的发展&am…

LeetCode 2258. 逃离火灾:BFS

【LetMeFly】2258.逃离火灾 力扣题目链接&#xff1a;https://leetcode.cn/problems/escape-the-spreading-fire/ 给你一个下标从 0 开始大小为 m x n 的二维整数数组 grid &#xff0c;它表示一个网格图。每个格子为下面 3 个值之一&#xff1a; 0 表示草地。1 表示着火的格…

【无标题(PC+WAP)花卉租赁盆栽绿植类pbootcms站模板

(PCWAP)花卉租赁盆栽绿植类pbootcms网站模板 PbootCMS内核开发的网站模板&#xff0c;该模板适用于盆栽绿植网站等企业&#xff0c;当然其他行业也可以做&#xff0c;只需要把文字图片换成其他行业的即可&#xff1b; PCWAP&#xff0c;同一个后台&#xff0c;数据即时同步&…

web —— css(1)

Web —— css基础 1. CSS样式表2. CSS的三种引入方式3. CSS 语法4. CSS 选择器4.1 元素选择器4.2 类选择器4.3 ID选择器4.4 属性选择器4.5 后代选择器4.6 子元素选择器4.7 伪类选择器4.8 分组选择器 5. 颜色和字体6. 显示方式display7. 盒子模型7.1 盒子模型 - 外边距塌陷7.2 盒…

LED显示屏像素技术

LED显示屏的像素技术是LED显示屏的核心技术之一&#xff0c;它决定了显示屏的清晰度、亮度和色彩表现。以下是一些常见的LED显示屏像素技术&#xff1a; 直插式LED显示屏像素技术&#xff1a;该技术采用LED灯珠直接插入到电路板上的方式&#xff0c;通过电路板上的电路连接实现…

怎么设置代理IP进行网络爬取呢?代理访问网络如何设置?

在如今网络爬虫广泛应用的年代&#xff0c;很多时候我们都会遇到需要使用代理IP进行网络爬取的情况。代理IP可以帮助我们隐藏真实的IP地址&#xff0c;从而保护我们的隐私和安全。那么&#xff0c;怎么设置代理IP进行网络爬取呢&#xff1f;代理访问网络如何设置&#xff1f;下…

mysql explain type 枚举

explain 查看 sql 查询是否走索引。 其中 type 的枚举如下 类型说明system表只有一行&#xff08;系统表&#xff09;&#xff0c;这是 const 类型的特例const单表中的某个固定的值eq_ref使用唯一索引等值查找一个行ref使用非唯一索引查找所有匹配某个单个值的行fulltext使用…

R语言和jsonlite库编写代码示例

R语言和jsonlite库来下载的程序。 r # 导入jsonlite库 library(jsonlite) # 设置代理主机和端口 proxy_host <- "" proxy_port <- # 使用httr库创建一个对象 proxy <- create_proxy(proxy_host, proxy_port) # 使用httr库的GET方法下载网页内容 url <…

原型链污染攻击

想要很清楚了理解原型链污染我们首先必须要弄清楚原型链这个概念 可以看这篇文章&#xff1a;对象的继承和原型链 目录 prototype和__proto__分别是什么&#xff1f; 原型链继承 原型链污染是什么 哪些情况下原型链会被污染&#xff1f; 例题1&#xff1a;Code-Breaking 2…

【原理篇】四、自定义starter

文章目录 1、案例分析2、业务功能的实现3、中途调试4、开启定时任务打印报表5、引入属性配置类&#xff0c;写活业务参数配置6、拦截器7、开启yml提示功能 做一个记录系统访客独立IP访问次数的功能&#xff0c;并把它自定义成一个starter&#xff0c;实现&#xff1a;在现有项目…

阿里云中的云服务器的ubuntu中的vim没有显示行号

没有行号&#xff1a; 在终端输入命令&#xff1a; vim ~/.vimrc set nu

c语言总是有小问题,是练的少吗?

c语言总是有小问题&#xff0c;是练的少吗&#xff1f; 题主说我做c语言的题目时候&#xff0c;是有思路的并且可以按照想法写下来&#xff0c;大体上看没有问题&#xff0c;但是到运行的时候总是不过关。就需要很长的时间找出那个细微的错误&#xff0c;这种细微的错误怎么才能…

【C++】——类与对象(二)

&#x1f383;个人专栏&#xff1a; &#x1f42c; 算法设计与分析&#xff1a;算法设计与分析_IT闫的博客-CSDN博客 &#x1f433;Java基础&#xff1a;Java基础_IT闫的博客-CSDN博客 &#x1f40b;c语言&#xff1a;c语言_IT闫的博客-CSDN博客 &#x1f41f;MySQL&#xff1a…

基于element-plus定义表格行内编辑配置化

文章目录 前言一、新增table组件二、使用步骤 前言 在 基于element-plus定义表单配置化 基础上&#xff0c;封装个Element-plus的table表格 由于表格不同于form组件&#xff0c;需自定义校验器&#xff0c;以下组件配置了单个校验&#xff0c;及提交统一校验方法&#xff0c;且…

python核心编程速记【笔记迁移】

笔记速记 1.python非常注重缩进&#xff0c;这是它的显著特征之一。 2.import相当于头文件声明模块。 3.利用type函数 type(a)可以查看当前变量类型。 isinstance可以比较两个数据类型并返回一个布尔值。 4.这里面的可直接使用and和or作为一个函数 5.python的算法比较贴合…