当前位置: 首页 > news >正文

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;
}

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

相关文章:

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

综述 首先&#xff0c;本篇文章所介绍的内容&#xff0c;已经有完整的实现&#xff0c;可以参考这里。 在微服务、DevOps和云平台流行的当下&#xff0c;使用一个高效的持续集成工具也是一个非常重要的事情。虽然市面上目前已经存在了比较成熟的自动化构建工具&#xff0c;比如…...

类图复习:类图简单介绍

入职新公司在看新项目的代码&#xff0c;所以借助类图梳理各个类之间的关系&#xff0c;奈何知识已经还给了老师&#xff0c;不得不重新学习下类图的相关知识&#xff0c;此处将相关内容记录下方便后续使用。 文章目录 类图语法类与类的关系画类图 类图语法 语法描述public-pr…...

【字符串】【双指针翻转字符串+快慢指针】Leetcode 151 反转字符串中单词【好】

【字符串】【双指针翻转字符串快慢指针】Leetcode 151 反转字符串中单词 解法1 双指针翻转字符串快慢指针更新数组大小 ---------------&#x1f388;&#x1f388;题目链接&#x1f388;&#x1f388;------------------- ---------------&#x1f388;&#x1f388;解答链接…...

3D Gaussian Splatting:用于实时的辐射场渲染

Kerbl B, Kopanas G, Leimkhler T, et al. 3d gaussian splatting for real-time radiance field rendering[J]. ACM Transactions on Graphics (ToG), 2023, 42(4): 1-14. 3D Gaussian Splatting 是 Siggraph 2023 的 Best Paper&#xff0c;法国团队在会议上展示了其实现的最…...

【nlp】文本处理的基本方法

文本处理的基本方法 1 什么是分词2 什么是命名实体识别3 什么是词性标准1 什么是分词 分词就是将连续的字序列按照一定的规范重新组合成词序列的过程。在英文的行文中,单词之间是以空格作为自然分界符的,而中文只是字、句和段能通过明显的分界符来简单划界,唯独词没有一个形…...

C++17 std::filesystem

std::filesystem 是 C17 标准引入的文件系统库&#xff0c;提供了一套用于处理文件和目录的 API。它主要包括以下几个核心类&#xff1a; std::filesystem::path&#xff1a;用于表示文件系统路径。它提供了一系列方法&#xff0c;允许你对路径进行各种操作&#xff0c;如拼接…...

JVM在线分析-解决问题的工具一(jinfo,jmap,jstack)

1. jinfo (base) PS C:\Users\zishi\Desktop> jinfo Usage:jinfo <option> <pid>(to connect to a running process)where <option> is one of:-flag <name> to print the value of the named VM flag #输出对应名称的参数-flag [|-]<n…...

[深度学习]不平衡样本的loss

不平衡样本的loss ”softmax“、”weighted softmax“、”focal“、”class-balanced“ 和 ”balanced softmax“ 都是用于多类分类任务的损失函数。它们之间的区别在于如何处理类别不均衡的问题。 Softmax 是常用的多类分类损失函数。它将输出分布转换为概率分布&#xff0c…...

【MySQL】表的增删改查(强化)

作者主页&#xff1a;paper jie_博客 本文作者&#xff1a;大家好&#xff0c;我是paper jie&#xff0c;感谢你阅读本文&#xff0c;欢迎一建三连哦。 本文录入于《MySQL》专栏&#xff0c;本专栏是针对于大学生&#xff0c;编程小白精心打造的。笔者用重金(时间和精力)打造&a…...

MyBatis-Plus--在xml中使用wrapper的方法

原文网址&#xff1a;MyBatis-Plus--在xml中使用wrapper的方法_IT利刃出鞘的博客-CSDN博客 简介 本文介绍MyBatis-Plus如何在xml中使用wrapper。 Service QueryWrapper<T> wrapper new QueryWrapper<T>(); wrapper.eq("r.room_id", vo.getRoomId())…...

Oracle RAC是啥?

Oracle RAC&#xff0c;全称是Oracle Real Application Cluster&#xff0c;翻译过来为Oracle真正的应用集群&#xff0c;它是Oracle提供的一个并行集群系统&#xff0c;由 Oracle Clusterware&#xff08;集群就绪软件&#xff09; 和 Real Application Cluster&#xff08;RA…...

springboot中定时任务cron不生效,fixedRate指定间隔失效,只执行一次的问题

在调试计算任务的时候&#xff0c;手动重置任务为初始状态&#xff0c;但是并没有重新开始计算&#xff0c;检查定时任务代码&#xff1a; 从Scheduled(fixedRate 120000)可以看到&#xff0c;应该是间隔120秒执行一次该定时任务&#xff0c;查看后台日志&#xff0c;并没有重…...

苹果手机发热发烫是什么原因?看完这篇你就知道了!

苹果手机以其卓越的用户体验和优秀的性能得到了广大用户的喜爱和追捧。在日常使用苹果手机时&#xff0c;我们可能会遇到手机发热发烫的情况。那么&#xff0c;苹果手机发热发烫是什么原因呢&#xff1f;小编将为大家解析这一问题的原因&#xff0c;并为您提供相应的解决方案&a…...

民安智库(第三方满意度调研公司):助力健身房提升客户满意度的秘密武器

在当今的健身行业&#xff0c;客户满意度已经成为衡量健身房竞争力的关键因素。为了准确了解客户的需求和反馈&#xff0c;某健身房委托民安智库对其进行客户满意度调查。 本次调查的主要目的是了解客户对健身房的满意度&#xff0c;包括对设施、课程、教练和服务的评价。调查…...

2011年09月01日 Go生态洞察:Go语言词法扫描与App Engine演示

&#x1f337;&#x1f341; 博主猫头虎&#xff08;&#x1f405;&#x1f43e;&#xff09;带您 Go to New World✨&#x1f341; &#x1f984; 博客首页——&#x1f405;&#x1f43e;猫头虎的博客&#x1f390; &#x1f433; 《面试题大全专栏》 &#x1f995; 文章图文…...

pytorch搭建squeezenet网络的整套工程(升级版)

上一篇当中&#xff0c;使用pytorch搭建了一个squeezenet&#xff0c;效果还行。但是偶然间发现了一个稍微改动的版本&#xff0c;拿来测试一下发现效果会更好&#xff0c;大概网络结构还是没有变&#xff0c;还是如下的第二个版本&#xff1a; 具体看网络结构代码&#xff1a…...

222. 完全二叉树的节点个数

题目链接&#xff1a;222. 完全二叉树的节点个数 需复刷 全代码&#xff1a; class Solution { public:int getnums(TreeNode* Node){if(Node NULL){return 0;}int leftnums getnums(Node ->left);int rightnums getnums(Node ->right);int cns 1 leftnums righ…...

adb and 软件架构笔记

Native Service&#xff0c;这是Android系统里的一种特色&#xff0c;就是通过C或是C代码写出来的&#xff0c;供Java进行远程调用的Remote Service&#xff0c;因为C/C代码生成的是Native代码&#xff08;机器代码&#xff09;&#xff0c;于是叫Native Service。 native服务…...

算术运算符、自增自减运算符、赋值运算符、关系运算符、逻辑运算符、三元运算符

1.算术运算符 public class OperatorDemo1 {public static void main(String[] args) {int a 10;int b 2;System.out.println(a b);System.out.println(a - b);System.out.println(a * b);System.out.println(a / b);System.out.println(5 / 2);System.out.println(5.0 / 2);…...

k8s 配置资源管理

配置资源管理 //Secret Secret 是用来保存密码、token、密钥等敏感数据的 k8s 资源&#xff0c;这类数据虽然也可以存放在 Pod 或者镜像中&#xff0c;但是放在 Secret 中是为了更方便的控制如何使用数据&#xff0c;并减少暴露的风险。 有三种类型&#xff1a; ●kubernetes.…...

iOS 26 携众系统重磅更新,但“苹果智能”仍与国行无缘

美国西海岸的夏天&#xff0c;再次被苹果点燃。一年一度的全球开发者大会 WWDC25 如期而至&#xff0c;这不仅是开发者的盛宴&#xff0c;更是全球数亿苹果用户翘首以盼的科技春晚。今年&#xff0c;苹果依旧为我们带来了全家桶式的系统更新&#xff0c;包括 iOS 26、iPadOS 26…...

应用升级/灾备测试时使用guarantee 闪回点迅速回退

1.场景 应用要升级,当升级失败时,数据库回退到升级前. 要测试系统,测试完成后,数据库要回退到测试前。 相对于RMAN恢复需要很长时间&#xff0c; 数据库闪回只需要几分钟。 2.技术实现 数据库设置 2个db_recovery参数 创建guarantee闪回点&#xff0c;不需要开启数据库闪回。…...

【JavaEE】-- HTTP

1. HTTP是什么&#xff1f; HTTP&#xff08;全称为"超文本传输协议"&#xff09;是一种应用非常广泛的应用层协议&#xff0c;HTTP是基于TCP协议的一种应用层协议。 应用层协议&#xff1a;是计算机网络协议栈中最高层的协议&#xff0c;它定义了运行在不同主机上…...

React第五十七节 Router中RouterProvider使用详解及注意事项

前言 在 React Router v6.4 中&#xff0c;RouterProvider 是一个核心组件&#xff0c;用于提供基于数据路由&#xff08;data routers&#xff09;的新型路由方案。 它替代了传统的 <BrowserRouter>&#xff0c;支持更强大的数据加载和操作功能&#xff08;如 loader 和…...

CVE-2020-17519源码分析与漏洞复现(Flink 任意文件读取)

漏洞概览 漏洞名称&#xff1a;Apache Flink REST API 任意文件读取漏洞CVE编号&#xff1a;CVE-2020-17519CVSS评分&#xff1a;7.5影响版本&#xff1a;Apache Flink 1.11.0、1.11.1、1.11.2修复版本&#xff1a;≥ 1.11.3 或 ≥ 1.12.0漏洞类型&#xff1a;路径遍历&#x…...

搭建DNS域名解析服务器(正向解析资源文件)

正向解析资源文件 1&#xff09;准备工作 服务端及客户端都关闭安全软件 [rootlocalhost ~]# systemctl stop firewalld [rootlocalhost ~]# setenforce 0 2&#xff09;服务端安装软件&#xff1a;bind 1.配置yum源 [rootlocalhost ~]# cat /etc/yum.repos.d/base.repo [Base…...

为什么要创建 Vue 实例

核心原因:Vue 需要一个「控制中心」来驱动整个应用 你可以把 Vue 实例想象成你应用的**「大脑」或「引擎」。它负责协调模板、数据、逻辑和行为,将它们变成一个活的、可交互的应用**。没有这个实例,你的代码只是一堆静态的 HTML、JavaScript 变量和函数,无法「活」起来。 …...

「全栈技术解析」推客小程序系统开发:从架构设计到裂变增长的完整解决方案

在移动互联网营销竞争白热化的当下&#xff0c;推客小程序系统凭借其裂变传播、精准营销等特性&#xff0c;成为企业抢占市场的利器。本文将深度解析推客小程序系统开发的核心技术与实现路径&#xff0c;助力开发者打造具有市场竞争力的营销工具。​ 一、系统核心功能架构&…...

uniapp 小程序 学习(一)

利用Hbuilder 创建项目 运行到内置浏览器看效果 下载微信小程序 安装到Hbuilder 下载地址 &#xff1a;开发者工具默认安装 设置服务端口号 在Hbuilder中设置微信小程序 配置 找到运行设置&#xff0c;将微信开发者工具放入到Hbuilder中&#xff0c; 打开后出现 如下 bug 解…...

tomcat指定使用的jdk版本

说明 有时候需要对tomcat配置指定的jdk版本号&#xff0c;此时&#xff0c;我们可以通过以下方式进行配置 设置方式 找到tomcat的bin目录中的setclasspath.bat。如果是linux系统则是setclasspath.sh set JAVA_HOMEC:\Program Files\Java\jdk8 set JRE_HOMEC:\Program Files…...