Jenkins Pipeline脚本优化:为Kubernetes应用部署增加状态检测

引言

在软件部署的世界中,Jenkins已经成为自动化流程的代名词。不断变化的技术环境要求我们持续改进部署流程以满足现代应用部署的需要。在本篇博客中,作为一位资深运维工程师,我将分享如何将Jenkins Pipeline进化至不仅能支持部署应用直至Running状态检测,同时也能兼顾Deployment和StatefulSet资源的轮询更新,并详细介绍滚动更新策略的配置方法。

初始Jenkins Pipeline分析

参照前文:Jenkins Pipeline 脚本优化实践:从繁琐到简洁,初始化pipeline如下:

pipeline {agent none // Use none at the top level, each stage will define its own agent.environment {REGISTRY = "swr.cn-north-4.myhuaweicloud.com/master-metaspace"KUBE_CONFIG = "--namespace=master-metaspace --context=master"KUBE_YAML_PATH = "/home/jenkins/workspace/yaml/master-metaspace"// Assume that 'data' is defined elsewhere or injected as a parameter.BASE_WORKSPACE = "xxxxxxx" // 定义一个基础工作空间路径}stages {stage("GetCode") {agent { label "build01" }steps {script {checkout scm: [$class: 'GitSCM',branches: [[name: env.branchName]],extensions: [[$class: 'CloneOption', depth: 1, noTags: false, shallow: true]],userRemoteConfigs: [[credentialsId: 'xxxx', url: env.gitHttpURL]]]}}}stage("Docker Builds") {parallel {stage('Build dataloader-game-ucenter') {agent { label "build01" }when { environment name: 'dataloader', value: 'true' }steps {buildAndPushDockerImage("dataloader-game-ucenter", env.data, env.BASE_WORKSPACE)}}stage('Build datawriter-game-ucenter') {agent { label "build01" }when { environment name: 'datawriter', value: 'true' }steps {buildAndPushDockerImage("datawriter-game-ucenter", env.data, env.BASE_WORKSPACE)}}stage('Build game-ucenter') {agent { label "build01" }when { environment name: 'game-ucenter', value: 'true' }steps {buildAndPushDockerImage("game-ucenter", env.data, env.BASE_WORKSPACE)}}}}stage('Development Deployment') {parallel {stage("Deploy datawriter-game-ucenter") {when { environment name: 'datawriter-game-ucenter', value: 'true' }agent { label  "huaweiyun-xx" }steps {deployToKubernetes("datawriter-game-ucenter")}}stage("Deploy dataloader-game-ucenter") {when { environment name: 'dataloader', value: 'true' }agent { label  "huaweiyun-xx" }steps {deployToKubernetes("dataloader-game-ucenter")}}stage("Deploy game-ucenter") {when { environment name: 'game-ucenter', value: 'true' }agent { label  "huaweiyun-xx" }steps {deployToKubernetes("game-ucenter")}}}}}
}// Define methods outside pipeline to avoid repetitiondef buildAndPushDockerImage(String imageName, String tag, String workspacePath) {sh "cd ${workspacePath} && echo 'Current directory: \$(pwd)'" // 使用基础工作空间变量sh "cd ${workspacePath}/${imageName}&& docker build --build-arg NODE_ENV=$imageName -t $REGISTRY/$imageName:$tag ."withCredentials([usernamePassword(credentialsId: 'hw-registry', passwordVariable: 'dockerPassword', usernameVariable: 'dockerUser')]) {sh "docker login -u $dockerUser -p $dockerPassword $REGISTRY"sh "docker push $REGISTRY/$imageName:$tag"}
}def deployToKubernetes(String kubernetesComponent) {String templateFile = "${KUBE_YAML_PATH}/${kubernetesComponent}.tpl"String outputFile = "${KUBE_YAML_PATH}/${kubernetesComponent}.yaml"sh "sed -e 's/{data}/$data/g' $templateFile > $outputFile"sh "sudo kubectl apply -f $outputFile $KUBE_CONFIG"
}

初始的Jenkins Pipeline定义了一个基本的CI/CD流程,涵盖了代码拉取、Docker镜像构建、推送及在Kubernetes环境中的部署。然而,流程中缺少了对部署状态的检查,这是在确保部署稳定性方面至关重要的一个环节。

进化 I:探针引入Deployment部署

现代应用部署不仅仅需要一个“部署到Kubernetes”的指令,更需要在部署后进行健康检查。对于Deployment类型的应用来说,我们需要在所有Pods运行并处于READY状态后才认为部署成功。

状态检测方法介绍

为此,我们引入了checkKubernetesResourceStatus方法来检查资源的状态。该方法通过kubectl的get命令和jsonpath查询输出来轮询检查ready副本数。如果指定时间内资源不达状态,则流程失败。

Jenkinsfile变更详解:

引入checkKubernetesResourceStatus方法来检测deployment各个阶段部署的状态。

def checkKubernetesResourceStatus(String deploymentName, String namespace) {int attempts = 30 // Set the number of retry attemptsint sleepTime = 10 // Set the sleep time between attempts in secondsString readyReplicasJsonPath = ".status.readyReplicas"for (int i = 1; i <= attempts; i++) {// Check the deployment statusString statusCheck = sh (script: "kubectl get deployment ${deploymentName} --namespace=${namespace} -o jsonpath=\"{${readyReplicasJsonPath}}\"",returnStdout: true).trim()// If the number of ready replicas is not empty and greater than 0if (statusCheck && statusCheck.isInteger() && statusCheck.toInteger() > 0) {echo "Deployment ${deploymentName} is ready."return} else {echo "Waiting for Deployment ${deploymentName} to be ready. Attempt ${i}/${attempts}"sleep sleepTime}}error "Deployment ${deploymentName} did not become ready after ${attempts} attempts"
}

Deploy game-ucenter stage为例:

                stage("Deploy game-ucenter") {when { environment name: 'game-ucenter', value: 'true' }agent { label  "xxxx" }steps {deployToKubernetes("game-ucenter")checkKubernetesResourceStatus("game-ucenter", "master-metaspace")}}

game-ucenter模板文件如下:

apiVersion: apps/v1
kind: Deployment
metadata:name: game-ucenter
spec:replicas: 1selector:matchLabels:app: game-ucentertemplate:metadata:labels:app: game-ucenterspec:containers:- name: game-ucenterimage: xxxxxx/xxxx/game-ucenter:{data}envFrom:- configMapRef:name: deployports:- containerPort: 80resources:requests:memory: "4096M"cpu: "2000m"limits:memory: "4096M"cpu: "2000m" livenessProbe:httpGet:scheme: HTTPpath: /test.htmlport: 80initialDelaySeconds: 20periodSeconds: 120successThreshold: 1failureThreshold: 3readinessProbe:httpGet:scheme: HTTPpath: /test.htmlport: 80initialDelaySeconds: 20periodSeconds: 120imagePullSecrets:                                              - name: xxxx
---apiVersion: v1
kind: Service
metadata:name: game-ucenterlabels:app: game-ucenter
spec:ports:- port: 80protocol: TCPtargetPort: 80selector:app: game-ucenter

image.png
尝试修改以下**replicas: 3 **重新运行以下pipeline:
image.png
也没有问题,pipeline 脚本有效!

进化 II:兼容StatefulSet的健康检查

考虑到某些应用可能采用StatefulSet作为工作负载类型,我们必须确保Jenkins Pipeline能够针对不同的工作负载类型执行健康检查。

状态检测兼容性改进

为了适配StatefulSet,我们对checkKubernetesResourceStatus方法做了略微修改,使其可以接受一个resourceType参数来区分资源类型,进而查询对应的状态字段,代码片段如下:

def checkKubernetesResourceStatus(String resourceName, String namespace, String resourceType) {int attempts = 30 // Set the number of retry attemptsint sleepTime = 10 // Set the sleep time between attempts in secondsString readyReplicasJsonPath = resourceType == "deployment" ? ".status.readyReplicas" : ".status.readyReplicas"for (int i = 1; i <= attempts; i++) {// Check the resource statusString statusCheck = sh (script: "kubectl get ${resourceType} ${resourceName} --namespace=${namespace} -o jsonpath=\"{${readyReplicasJsonPath}}\"",returnStdout: true).trim()// If the number of ready replicas is not empty and equal to the desired numberif (statusCheck && statusCheck.isInteger() && statusCheck.toInteger() > 0) {echo "${resourceType} ${resourceName} is ready."return} else {echo "Waiting for ${resourceType} ${resourceName} to be ready. Attempt ${i}/${attempts}"sleep(sleepTime)}}error "${resourceType} ${resourceName} did not become ready after ${attempts} attempts"
}

修改game-ucenter stage:

                stage("Deploy game-ucenter") {when { environment name: 'game-ucenter', value: 'true' }agent { label  "k8s-node-06" }steps {deployToKubernetes("game-ucenter")checkKubernetesResourceStatus("game-ucenter", "master-metaspace", "deployment")}}

创建一个statefulset datawriter-game-ucenter stage:

                stage("Deploy datawriter-game-ucenter") {when { environment name: 'datawriter-game-ucenter', value: 'true' }agent { label  "xxxxx" }steps {deployToKubernetes("datawriter-game-ucenter")checkKubernetesResourceStatus("datawriter-game-ucenter", "master-metaspace", "statefulset")}}

image.png
注意:我这里截图还是用了game-ucenter做的测试,其实我想用我的datawriter-game-ucenter,but这个服务是一个node应用没有没有livenessProbe readinessProbe,所以截图我还是使用了game-ucenter!

进化 III:引入滚动更新策略配置和检测

当我们更新Deployment资源时,通常会采用滚动更新策略,以逐步替换旧Pods,最小化部署时的中断。

更新策略检测逻辑

def checkDeploymentUpdateStatus(String deploymentName, String namespace) {int attempts = 30 // Set the number of retry attemptsint sleepTime = 10 // Set the sleep time between attempts in secondsecho "Checking the update status of Deployment: ${deploymentName}"for (int i = 1; i <= attempts; i++) {String updateStatus = sh (script: "kubectl rollout status deployment/${deploymentName} --namespace=${namespace}",returnStdout: true).trim()if (updateStatus.contains("successfully rolled out")) {echo "Update status: ${updateStatus}"return} else {echo "Waiting for Deployment ${deploymentName} to successfully roll out. Attempt ${i}/${attempts}"sleep(sleepTime)}}error "Deployment ${deploymentName} did not successfully roll out after ${attempts} attempts"
}
  1. 我们增加了checkDeploymentUpdateStatus方法,该方法通过kubectl命令rollout status监控Deployment的更新状态。
  2. 当检测到successfully rolled out时,表示滚动更新成功。
  3. 如果在给定时间内更新没有成功,则流程将失败。

继续考虑一下如果statefulset多实例呢?不想写两个了整合成一个方法如下:

def checkRolloutStatus(String resourceName, String namespace, String resourceType) {int attempts = 30 // Set the number of retry attemptsint sleepTime = 10 // Set the sleep time between attempts in secondsif (!(resourceType in ["deployment", "statefulset"])) {error "Unknown resource type: ${resourceType}. Only 'deployment' and 'statefulset' are supported."}echo "Checking the update status of ${resourceType} '${resourceName}' in namespace '${namespace}'"for (int i = 1; i <= attempts; i++) {String rolloutCommand = "kubectl rollout status ${resourceType}/${resourceName} --namespace=${namespace}"String updateStatus = sh (script: rolloutCommand,returnStdout: true).trim()if (updateStatus.contains("successfully rolled out") || updateStatus.contains("partitioned roll out complete"))  {echo "Update status: ${updateStatus}"return} else {echo "Waiting for ${resourceType} '${resourceName}' to successfully roll out. Attempt ${i}/${attempts}."sleep(sleepTime)}}error "${resourceType} '${resourceName}' did not successfully roll out after ${attempts} attempts in namespace '${namespace}'"
}

Jenkinsfile更新实现

经过上述进化,Jenkinsfile中现在包含了完整的部署状态检查逻辑,以应对不同类型资源的部署监控需求。

pipeline {agent none // Use none at the top level, each stage will define its own agent.environment {REGISTRY = "ccr.ccs.tencentyun.com/xxxxx"KUBE_CONFIG = "--namespace=master-metaspace"KUBE_YAML_PATH = "/home/jenkins/workspace/yaml/master-metaspace"// Assume that 'data' is defined elsewhere or injected as a parameter.BASE_WORKSPACE = "xxxxxx" // 定义一个基础工作空间路径}stages {stage("GetCode") {agent { label "build01" }steps {script {checkout scm: [$class: 'GitSCM',branches: [[name: env.branchName]],extensions: [[$class: 'CloneOption', depth: 1, noTags: false, shallow: true]],userRemoteConfigs: [[credentialsId: 'xxxxx', url: env.gitHttpURL]]]}}}stage("Docker Builds") {parallel {stage('Build dataloader-game-ucenter') {agent { label "build01" }when { environment name: 'dataloader-game-ucenter', value: 'true' }steps {buildAndPushDockerImage("dataloader-game-ucenter", env.data, env.BASE_WORKSPACE)}}stage('Build datawriter-game-ucenter') {agent { label "build01" }when { environment name: 'datawriter-game-ucenter', value: 'true' }steps {buildAndPushDockerImage("datawriter-game-ucenter", env.data, env.BASE_WORKSPACE)}}stage('Build game-ucenter') {agent { label "build01" }when { environment name: 'game-ucenter', value: 'true' }steps {buildAndPushDockerImage("game-ucenter", env.data, env.BASE_WORKSPACE)}}}}stage('Development Deployment') {parallel {stage("Deploy datawriter-game-ucenter") {when { environment name: 'datawriter-game-ucenter', value: 'true' }agent { label  "xxxx" }steps {deployToKubernetes("datawriter-game-ucenter")checkKubernetesResourceStatus("datawriter-game-ucenter", "master-metaspace", "statefulset")checkRolloutStatus("datawriter-game-ucenter", "master-metaspace", "statefulset")}}stage("Deploy dataloader-game-ucenter") {when { environment name: 'dataloader-game-ucenter', value: 'true' }agent { label  "xxxx" }steps {deployToKubernetes("dataloader-game-ucenter")checkKubernetesResourceStatus("dataloader-game-ucenter", "master-metaspace", "statefulset")}}stage("Deploy game-ucenter") {when { environment name: 'game-ucenter', value: 'true' }agent { label  "xxxx" }steps {deployToKubernetes("game-ucenter")checkRolloutStatus("game-ucenter", "master-metaspace", "deployment")checkKubernetesResourceStatus("game-ucenter", "master-metaspace", "deployment")}}}}}
}// Define methods outside pipeline to avoid repetitiondef buildAndPushDockerImage(String imageName, String tag, String workspacePath) {sh "cd ${workspacePath} && echo 'Current directory: \$(pwd)'" // 使用基础工作空间变量sh "cd ${workspacePath}/${imageName}&& docker build --build-arg NODE_ENV=game-ucenter -t $REGISTRY/$imageName:$tag ."withCredentials([usernamePassword(credentialsId: 'xxxxx', passwordVariable: 'dockerPassword', usernameVariable: 'dockerUser')]) {sh "docker login -u $dockerUser -p $dockerPassword $REGISTRY"sh "docker push $REGISTRY/$imageName:$tag"}
}def deployToKubernetes(String kubernetesComponent) {String templateFile = "${KUBE_YAML_PATH}/${kubernetesComponent}.tpl"String outputFile = "${KUBE_YAML_PATH}/${kubernetesComponent}.yaml"sh "sed -e 's/{data}/$data/g' $templateFile > $outputFile"sh "sudo kubectl apply -f $outputFile $KUBE_CONFIG"
}def checkRolloutStatus(String resourceName, String namespace, String resourceType) {int attempts = 30 // 设置重试次数int sleepTime = 10 // 设置重试间隔时间(秒)if (!(resourceType in ["deployment", "statefulset"])) {error "未知资源类型:${resourceType}。只支持 'deployment' 和 'statefulset' 。"}echo "正在检查${resourceType} '${resourceName}' 在命名空间 '${namespace}' 的更新状态"for (int i = 1; i <= attempts; i++) {String rolloutCommand = "kubectl rollout status ${resourceType}/${resourceName} --namespace=${namespace}"try {String updateStatus = sh (script: rolloutCommand,returnStdout: true).trim()// 添加对 "partitioned roll out complete" 状态的检查if (updateStatus.contains("successfully rolled out") || updateStatus.contains("partitioned roll out complete")) {echo "更新状态:${updateStatus}"return} else {echo "等待 ${resourceType} '${resourceName}' 成功发布。尝试次数:${i}/${attempts}。"sleep(sleepTime)}} catch (Exception e) {echo "获取更新状态时发生错误:${e.getMessage()}。尝试次数:${i}/${attempts}。"sleep(sleepTime)}}error "${resourceType} '${resourceName}' 在命名空间 '${namespace}' 内未能在 ${attempts} 次尝试之后成功发布"
}
def checkKubernetesResourceStatus(String resourceName, String namespace, String resourceType) {int attempts = 30 // Set the number of retry attemptsint sleepTime = 10 // Set the sleep time between attempts in secondsString readyReplicasJsonPath = resourceType == "deployment" ? ".status.readyReplicas" : ".status.readyReplicas"for (int i = 1; i <= attempts; i++) {// Check the resource statusString statusCheck = sh (script: "kubectl get ${resourceType} ${resourceName} --namespace=${namespace} -o jsonpath=\"{${readyReplicasJsonPath}}\"",returnStdout: true).trim()// If the number of ready replicas is not empty and equal to the desired numberif (statusCheck && statusCheck.isInteger() && statusCheck.toInteger() > 0) {echo "${resourceType} ${resourceName} is ready."return} else {echo "Waiting for ${resourceType} ${resourceName} to be ready. Attempt ${i}/${attempts}"sleep(sleepTime)}}error "${resourceType} ${resourceName} did not become ready after ${attempts} attempts"
}// 更新后的Jenkins Pipeline代码详细定义参照本文开头给出的代码

image.png

总结

本篇博客通过对Jenkins Pipeline的进化过程展开讲解,展现了如何从简单的部署任务转变为一个健壮且兼顾各类工作负载状态监测的CI/CD流程。我们强化了状态检测的逻辑,引入了更新策略的检测,并保持了对不同Kubernetes资源类型的兼容性。这些改进确保了自动化流程能够与现代部署实践保持同步,给运维团队带来极大便利,并最大化地保障了部署的可靠性。

后记

由于篇幅限制,本篇文章未作其他更详细演示。然而,在实际应用中,运维团队可以根据自己的具体需求和环境进一步丰富和细化每个步骤的实现,确保Pipeline的健壮性和高可用性,以适应不断变化的技术挑战。

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

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

相关文章

Apache Flink 结合 Kafka 构建端到端的 Exactly-Once 处理

文章目录&#xff1a; Apache Flink 应用程序中的 Exactly-Once 语义Flink 应用程序端到端的 Exactly-Once 语义示例 Flink 应用程序启动预提交阶段在 Flink 中实现两阶段提交 Operator总结 Apache Flink 自2017年12月发布的1.4.0版本开始&#xff0c;为流计算引入了一个重要的…

一文教你如何使用 MongoDB 和 HATEOAS 创建 REST Web 服务

作者 | Ion Pascari译者 | 天道酬勤 责编 | 徐威龙封图| CSDN 下载于视觉中国最近&#xff0c;作者在把HATEOAS实现到REST Web服务时遇到了一件有趣的事情&#xff0c;而且他也很幸运地尝试了一个名为MongoDB的NoSQL数据库&#xff0c;他发现该数据库在许多不需要管理实务的不同…

使用NGINX作为HTTPS正向代理服务器

NGINX主要设计作为反向代理服务器&#xff0c;但随着NGINX的发展&#xff0c;它同样能作为正向代理的选项之一。正向代理本身并不复杂&#xff0c;而如何代理加密的HTTPS流量是正向代理需要解决的主要问题。本文将介绍利用NGINX来正向代理HTTPS流量两种方案&#xff0c;及其使用…

IoT SaaS加速器——助力阿尔茨海默病人护理

场景介绍 阿尔茨海默病&#xff0c;是导致中老年人认知功能障碍的最常见疾病之一&#xff0c;是发生在老年期及老年前期的一种原发性退行性脑病。据估计&#xff0c;全世界痴呆症患者数量为4700万&#xff0c;到2030年将达到7500万人。痴呆症患者数量到2050年预计将是现在的近…

一个数据科学家需要哪些核心工具包?

作者 | Rebecca Vickery译者 | 天道酬勤 责编 | 徐威龙封图| CSDN 下载于视觉中国数据科学家的主要作用是将机器学习、统计方法和探索性分析应用到数据中&#xff0c;来提取见解并帮助制定决策。 编程和计算工具的使用对该角色来说必不可少。 实际上&#xff0c;许多人都用这句…

Java-静态方法、非静态方法

// 学生类 public class Student {// 静态方法 staticpublic static void say01(){System.out.println("学生01 静态方法说话了");}// 非静态方法public void say02(){System.out.println("学生02 非静态方法说话了");} }静态方法、非静态方法 public cla…

SpringBoot2.x Flowable 6.4.2 开源项目

文章目录一、项目服务端初始化1. 创建数据库2. 初始化表结构.3. 表结构补充4. 配置文件修改5. 下载依赖6. 异常解决7. 启动服务端二、前端初始化2.1. 安装Node(V12.x.x以上)和NPM(V6.x.x以上)2.2. 安装淘宝镜像2.2. 初始化前端项目2.3. 启动项目2.4. web登录页面2.5. 效果图三、…

MongoDB 4.2 新特性解读

云数据库 MongoDB 版 基于飞天分布式系统和高性能存储&#xff0c;提供三节点副本集的高可用架构&#xff0c;容灾切换&#xff0c;故障迁移完全透明化。并提供专业的数据库在线扩容、备份回滚、性能优化等解决方案。 了解更多 MongoDB World 2019 上发布新版本 MongoDB 4.2…

Java-类与对象的创建

// 学生类 public class Student {// 属性String name; // 默认 nullint age; // 默认 0// 方法public void study(){System.out.println(this.name " 在学习");} }public class Application {public static void main(String[] args) {// 实例化后会返回一个自己…

Spark3.0发布了,代码拉过来,打个包,跑起来!| 附源码编译

作者 | 敏叔V587责编 | 徐威龙封图| CSDN 下载于视觉中国Spark3.0已经发布有一阵子了&#xff0c;官方发布了预览版&#xff0c;带来了一大波更新&#xff0c;对于我们程序员来说&#xff0c;首先当然是代码拉过来&#xff0c;打个包&#xff0c;跑起来&#xff01;&#xff01…

MySQL单表数据不要超过500万行:是经验数值,还是黄金铁律?

今天&#xff0c;探讨一个有趣的话题&#xff1a;MySQL 单表数据达到多少时才需要考虑分库分表&#xff1f;有人说 2000 万行&#xff0c;也有人说 500 万行。那么&#xff0c;你觉得这个数值多少才合适呢&#xff1f; 曾经在中国互联网技术圈广为流传着这么一个说法&#xff1…

Java-构造器

一个类即使什么都不写&#xff0c;它也会存在一个方法 package oop.demo02;/*** author blake.wang* date 2021-04-19 18:58*/ public class Person {// 一个类即使什么都不写&#xff0c;它也会存在一个方法&#xff0c;具体可以看一个空 类 的编译后的class文件// 显示的定…

IntelliJ IDEA 2020 基础设置

文章目录1. 字体设置2. 编码设置3. jdk设置4. 自动引入包和删除无用引入的包5. 打开文件左右联动定位1. 字体设置 菜单字体 编辑区 控制台 收缩自如 2. 编码设置 3. jdk设置 4. 自动引入包和删除无用引入的包 5. 打开文件左右联动定位

跟面试官侃半小时MySQL事务隔离性,从基本概念深入到实现

来源 | 阿丸笔记提到MySQL的事务&#xff0c;我相信对MySQL有了解的同学都能聊上几句&#xff0c;无论是面试求职&#xff0c;还是日常开发&#xff0c;MySQL的事务都跟我们息息相关。而事务的ACID&#xff08;即原子性Atomicity、一致性Consistency、隔离性Isolation、持久性D…

Java-封装

// 类 public class Student {// 属性私有private String name; // 名字private int id; // 学号private char sex; // 性别private int age; // 年龄// 提供一些可以操作这个属性的方法// 提供一些 public 的 get \ set 方法// get 获得这个数据public String getName(){r…

一条数据的漫游奇遇记

数据库存储引擎是一个有历史的技术&#xff0c;经过数十年的发展&#xff0c;已经出现很多优秀成熟的产品。阿里巴巴 X-Engine 团队撰写的论文 "X-Engine: An Optimized Storage Engine for Large-scale E-Commerce Transaction Processing"&#xff0c;详细讲述了团…

idea 编辑区设置

文章目录1. 显示行号2. tabs位置3. tabs排序4.tabs数量5.1. 显示行号 2. tabs位置 3. tabs排序 鼠标按住上下拖拽 按字母排序 4.tabs数量 5.

支付宝玉伯:从前端到体验,如何把格局做大

国内的前端行业&#xff0c;是一个群星璀璨&#xff0c;同时又有些纷纷扰扰的圈子。很多初出茅庐的年轻人怀着改变世界的梦想&#xff0c;谁也不服谁。不过&#xff0c;有一些为前端领域做出贡献的拓荒者几乎受到所有人的尊敬&#xff0c;玉伯就是这些拓荒者中的一员。 如今&am…

Java-继承

一个父类可以有多个子类&#xff0c;但是一个子类只能有一个父类 Person // 在java中&#xff0c;所有的类&#xff0c;都直接或者间接继承Object类 // person 人 父类 public class Person {// public > protected > default > private// public// protected --…

2019报告:AI程序员人才需求暴涨35倍!每10个公司就有6个人才缺口

就在最近&#xff0c;斯坦福大学AI研究所发布了《 2019 年 AI 指数报告》&#xff0c;其中一条数字让笔者震惊&#xff1a;58&#xff05; 的受访大公司表示&#xff0c;2019 年至少在一个业务部门采用了 AI那就是说&#xff0c;近半数以上的公司都需要AI人才&#xff01;但是现…