基于jenkins+docker实现CI/CD实践

news2024/11/20 3:22:28

项目简介

利用 Jenkins、Docker、SonarQube 和 Harbor 技术,搭建一个完整的 CI/CD 管道,实现持续集成、持续交付和持续部署的流程。通过自动化构建、测试、代码质量检查和容器化部署,将开发人员从繁琐的手动操作中解放出来,提高团队的开发效率、软件质量和安全性,实现持续更新迭代和持续部署交付。

CICD流程图

jenkins.drawio.png

流程说明

  1. 开发人员将代码提交到Gitlab代码仓库时,gitlab请求jenkins的webhook地址,触发持续构建和持续部署流程。
  2. Jenkins从Gitlab中拉取项目源码,编译并打成jar包,然后调用SonarQube完成代码扫描。
  3. 扫描完成调用docker打包成容器镜像,并推送至Harbor镜像仓库。
  4. Jenkins发送SSH远程命令,让生产部署服务器到Harbor私有仓库拉取镜像到本地,然后创建容器。
  5. Jenkins完成CICD流程后,将结果邮件通知给开发和运维人员。
  6. 用户访问项目服务器。

服务器列表

服务器名称主机名IP部署服务
代码托管服务器gitlab192.168.10.72Gitlab
持续集成服务器jenkins192.168.10.73Jenkins、Maven、Docker
代码审查服务器sonarqube192.168.10.71SonarQube
镜像仓库服务器harbor192.168.10.100Docker、harbor
服务部署服务器springboot192.168.10.74Docker

项目代码仓库地址

gitee:https://gitee.com/cuiliang0302/sprint_boot_demo
github:https://github.com/cuiliang0302/sprint-boot-demo

服务部署(rpm方式)

gitlab部署

参考文档:https://www.cuiliangblog.cn/detail/section/92727905

jenkins部署

参考文档:https://www.cuiliangblog.cn/detail/section/15130009

docker部署

参考文档:https://www.cuiliangblog.cn/detail/section/26447182

harbor部署

参考文档:https://www.cuiliangblog.cn/detail/section/15189547

SonarQube部署

参考文档:https://www.cuiliangblog.cn/detail/section/131467837

harbor项目权限配置

创建项目

Harbor的项目分为公开和私有的:
公开项目:所有用户都可以访问,通常存放公共的镜像,默认有一个library公开项目。
私有项目:只有授权用户才可以访问,通常存放项目本身的镜像。 我们可以为微服务项目创建一个新的项目
image.png

创建用户

创建一个普通用户cuiliang。
image.png

配置项目用户权限

在spring_boot_demo项目中添加普通用户cuiliang,并设置角色为开发者。
image.png
权限说明

角色权限
访客对项目有只读权限
开发人员对项目有读写权限
维护人员对项目有读写权限、创建webhook权限
项目管理员出上述外,还有用户管理等权限

上传下载镜像测试

可参考文章https://www.cuiliangblog.cn/detail/section/15189547,此处不再赘述。

gitlab项目权限配置

具体gitlab权限配置参考文档:https://www.cuiliangblog.cn/detail/section/131513569
创建开发组develop,用户cuiliang,项目springboot demo

创建组

管理员用户登录,创建群组,组名称为develop,组权限为私有
image.png

创建项目

创建sprint boot demo项目,并指定develop,项目类型为私有
image.png

创建用户

创建一个普通用户cuiliang
image.png

用户添加到组中

将cuiliang添加到群组develop中,cuiliang角色为Developer
image.png

开启分支推送权限

image.png

用户权限验证

使用任意一台机器模拟开发人员拉取代码,完成开发后推送至代码仓库。
拉取仓库代码

[root@tiaoban opt]# git clone https://gitee.com/cuiliang0302/sprint_boot_demo.git
正克隆到 'sprint_boot_demo'...
remote: Enumerating objects: 69, done.
remote: Counting objects: 100% (69/69), done.
remote: Compressing objects: 100% (54/54), done.
remote: Total 69 (delta 15), reused 0 (delta 0), pack-reused 0
接收对象中: 100% (69/69), 73.15 KiB | 1.49 MiB/s, 完成.
处理 delta 中: 100% (15/15), 完成.
[root@tiaoban opt]# cd sprint_boot_demo/
[root@tiaoban sprint_boot_demo]# ls
email.html  Jenkinsfile  LICENSE  mvnw  mvnw.cmd  pom.xml  readme.md  sonar-project.properties  src  test

推送至gitlab仓库

# 修改远程仓库地址
[root@tiaoban sprint_boot_demo]# git remote set-url origin http://192.168.10.72/develop/sprint-boot-demo.git
[root@tiaoban sprint_boot_demo]# git remote -v
origin  http://192.168.10.72/develop/sprint-boot-demo.git (fetch)
origin  http://192.168.10.72/develop/sprint-boot-demo.git (push)
# 推送代码至gitlab
[root@tiaoban sprint_boot_demo]# git push --set-upstream origin --all
Username for 'http://192.168.10.72': cuiliang
Password for 'http://cuiliang@192.168.10.72': 
枚举对象中: 55, 完成.
对象计数中: 100% (55/55), 完成.
使用 4 个线程进行压缩
压缩对象中: 100% (34/34), 完成.
写入对象中: 100% (55/55), 71.51 KiB | 71.51 MiB/s, 完成.
总共 55(差异 10),复用 52(差异 9),包复用 0
To http://192.168.10.72/develop/sprint-boot-demo.git
 * [new branch]      main -> main
分支 'main' 设置为跟踪 'origin/main'

查看验证
image.png

jenkins流水线配置

拉取gitlab仓库代码

具体步骤可参考文档:https://www.cuiliangblog.cn/detail/section/127410630,此处以账号密码验证为例,并启用webhook配置。
jenkins流水线配置如下
image.png
拉取代码部分的jenkinsfile如下

pipeline {
    agent any
    stages {
        stage('拉取代码') {
            environment {
                // gitlab仓库信息
                GITLAB_CRED = "gitlab-cuiliang-password"
                GITLAB_URL = "http://192.168.10.72/develop/sprint-boot-demo.git"
            }
            steps {
                echo '开始拉取代码'
                checkout scmGit(branches: [[name: '*/main']], extensions: [], userRemoteConfigs: [[credentialsId: "${GITLAB_CRED}", url: "${GITLAB_URL}"]])
                echo '拉取代码完成'
            }
        }
    }
}

当git仓库提交代码后,Gitlab会自动请求Jenkins的webhook地址,自动触发流水线,执行结果如下:
image.png

Maven打包编译

具体步骤可参考文档:https://www.cuiliangblog.cn/detail/section/131898197
打包编译部分的jenkinsfile如下

pipeline {
    agent any
    stages {
        stage('拉取代码') {
            ……
        }

        stage('打包编译') {
            steps {
                echo '开始打包编译'
                sh 'mvn clean package'
                echo '打包编译完成'
            }
        }
    }
}

触发流水线结果如下
image.png

SonarQube代码审查

具体步骤可参考文档:https://www.cuiliangblog.cn/detail/section/165534414
代码审查阶段的jenkinsfile如下

pipeline {
    agent any
    stages {
        stage('拉取代码') {
            ……
        }

        stage('打包编译') {
            ……
        }

        stage('代码审查') {
            environment {
                // SonarQube信息
                SONARQUBE_SCANNER = "SonarQubeScanner"
                SONARQUBE_SERVER = "SonarQubeServer"
            }
            steps{
                echo '开始代码审查'
                script {
                    def scannerHome = tool "${SONARQUBE_SCANNER}"
                    withSonarQubeEnv("${SONARQUBE_SERVER}") {
                        sh "${scannerHome}/bin/sonar-scanner"
                    }
                }
                echo '代码审查完成'
            }
        }
    }
}

触发流水线结果如下
image.png
代码审查结果如下
image.png

构建并推送镜像至仓库

具体步骤可参考文档:https://www.cuiliangblog.cn/detail/section/166573065
构建并推送镜像的jenkinsfile如下

pipeline {
    agent any
    stages {
        stage('拉取代码') {
            ……
        }

        stage('打包编译') {
            ……
        }

        stage('代码审查') {
            ……
        }

        stage('构建镜像') {
            environment {
                // harbor信息
                HARBOR_CRED = "harbor-cuiliang-password"
                HARBOR_URL = "harbor.local.com"
                HARBOR_PROJECT = "spring_boot_demo"
                // 镜像信息
                IMAGE_APP = "demo"
                IMAGE_TAG = sh(script: "git rev-parse --short HEAD", returnStdout: true).trim()
                IMAGE_NAME = "${HARBOR_URL}/${HARBOR_PROJECT}/${IMAGE_APP}:${IMAGE_TAG}"
            }
            steps {
                echo '开始构建镜像'
                script {
                    docker.build "${IMAGE_NAME}"
                }
                echo '构建镜像完成'
                echo '开始推送镜像'
                script {
                    docker.withRegistry("https://${HARBOR_URL}", "${HARBOR_CRED}") {
                        docker.image("${IMAGE_NAME}").push()
                    }
                }
                echo '推送镜像完成'
                echo '开始删除镜像'
                script {
                    sh "docker rmi -f ${IMAGE_NAME}"
                }
                echo '删除镜像完成'
            }
        }
    }
}

触发流水线结果如下
image.png
查看harbor镜像仓库,已上传镜像
image.png

docker运行服务

远程执行命令具体内容可参考文档:https://www.cuiliangblog.cn/detail/section/166296541
部署运行阶段的jenkinsfile如下

pipeline {
    agent any
    environment {
        // 全局变量
        HARBOR_CRED = "harbor-cuiliang-password"
        IMAGE_NAME = ""
        IMAGE_APP = "demo"
    } 
    stages {
        stage('拉取代码') {
            ……
        }

        stage('打包编译') {
            ……
        }

        stage('代码审查') {
            ……
        }

        stage('构建镜像') {
            ……
        }

        stage('项目部署') {
            environment {
                // 目标主机信息
                HOST_NAME = "springboot1"
            }
            steps {
                echo '开始部署项目'
                // 获取harbor账号密码
                withCredentials([usernamePassword(credentialsId: "${HARBOR_CRED}", passwordVariable: 'HARBOR_PASSWORD', usernameVariable: 'HARBOR_USERNAME')]) {
                    // 执行远程命令
                    sshPublisher(publishers: [sshPublisherDesc(configName: "${HOST_NAME}", transfers: [sshTransfer(
                        cleanRemote: false, excludes: '', execCommand: "sh -x /opt/jenkins/springboot/deployment.sh ${HARBOR_USERNAME} ${HARBOR_PASSWORD} ${IMAGE_NAME} ${IMAGE_APP}", 
                        execTimeout: 120000, flatten: false, makeEmptyDirs: false, noDefaultExcludes: false, patternSeparator: '[, ]+', remoteDirectory: '/opt/jenkins/springboot', 
                        remoteDirectorySDF: false, removePrefix: '', sourceFiles: 'deployment.sh')], usePromotionTimestamp: false, useWorkspaceInPromotion: false, verbose: false
                    )])
                }
                echo '部署项目完成'
            }
        }
    }
}    

触发流水线后运行结果如下
image.png
登录springboot服务器验证

[root@springboot ~]# docker ps
CONTAINER ID   IMAGE                                            COMMAND                CREATED              STATUS                          PORTS                                       NAMES
e80896487125   harbor.local.com/spring_boot_demo/demo:8880a30   "java -jar /app.jar"   About a minute ago   Up About a minute (unhealthy)   0.0.0.0:8888->8888/tcp, :::8888->8888/tcp   demo
[root@springboot ~]# curl 127.0.0.1:8888/
<h1>Hello SpringBoot</h1><p>Version:v2 Env:test</p>[root@springboot1 ~]# 
[root@springboot ~]# ls /opt/jenkins/springboot/
deployment.sh  Dockerfile  email.html  Jenkinsfile  LICENSE  mvnw  mvnw.cmd  pom.xml  readme.md  sonar-project.properties  src  target  test

添加邮件通知推送

发送邮件配置具体内容可参考文档:https://www.cuiliangblog.cn/detail/section/133029974
在项目根路径下新增email.html文件,内容如下

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>${ENV, var="JOB_NAME"}-第${BUILD_NUMBER}次构建日志</title>
    <style>
      body {
        font-family: Arial, sans-serif;
        line-height: 1.6;
        margin: 0;
        padding: 0;
      }
      .container {
        max-width: 1000px;
        margin: 20px auto;
        padding: 20px;
        border: 1px solid #ccc;
        border-radius: 5px;
      }
      .container h2 {
        text-align: center;
      }
      .logo img {
        max-width: 150px;
        height: auto;
      }
      .content {
        padding: 20px;
        background-color: #f9f9f9;
        border-radius: 5px;
      }
      .footer {
        margin-top: 20px;
        text-align: center;
      }
    </style>
  </head>
  <body>
    <div class="container">
      <div class="content">
        <h2>Jenkins ${PROJECT_NAME}项目构建结果</h2>
        <p>尊敬的用户:</p>
        <p>${PROJECT_NAME}项目构建结果为<span style="color:red;font-weight: bold;">${BUILD_STATUS}</span>,以下是详细信息:</p>
        <h4>构建信息</h4>
        <hr/>
        <ul>
          <li>项目名称:${PROJECT_NAME}</li>
          <li>构建编号:第${BUILD_NUMBER}次构建</li>
          <li>触发原因:${CAUSE}</li>
          <li>构建状态:${BUILD_STATUS}</li>
          <li>构建日志:<a href="${BUILD_URL}console">${BUILD_URL}console</a></li>
          <li>构建Url:<a href="${BUILD_URL}">${BUILD_URL}</a></li>
          <li>工作目录:<a href="${PROJECT_URL}ws">${PROJECT_URL}ws</a></li>
          <li>项目Url:<a href="${PROJECT_URL}">${PROJECT_URL}</a></li>
        </ul>
        <h4>失败用例</h4>
        <hr/>
        <p>$FAILED_TESTS</p>
        <h4>最近提交</h4>
        <hr/>
        <ul>
          ${CHANGES_SINCE_LAST_SUCCESS, reverse=true, format="%c", changesFormat="<li>%d [%a] %m</li>"}
        </ul>
        <h4>提交详情</h4>
        <hr/>
        <p><a href="${PROJECT_URL}changes">${PROJECT_URL}changes</a></p>
        <p style="margin-top:50px">如有任何疑问或需要帮助,请随时联系我们。</p>
      </div>
      <div class="footer">
        <p>此为系统自动发送邮件,请勿回复。</p>
      </div>
    </div>
  </body>
</html>

完整的jenkinsfile如下

pipeline {
    agent any
    environment {
        // 全局变量
        HARBOR_CRED = "harbor-cuiliang-password"
        IMAGE_NAME = ""
        IMAGE_APP = "demo"
    } 
    stages {
        stage('拉取代码') {
            environment {
                // gitlab仓库信息
                GITLAB_CRED = "gitlab-cuiliang-password"
                GITLAB_URL = "http://192.168.10.72/develop/sprint-boot-demo.git"
            }
            steps {
                echo '开始拉取代码'
                checkout scmGit(branches: [[name: '*/main']], extensions: [], userRemoteConfigs: [[credentialsId: "${GITLAB_CRED}", url: "${GITLAB_URL}"]])
                echo '拉取代码完成'
            }
        }

        stage('打包编译') {
            steps {
                echo '开始打包编译'
                sh 'mvn clean package'
                echo '打包编译完成'
            }
        }

        stage('代码审查') {
            environment {
                // SonarQube信息
                SONARQUBE_SCANNER = "SonarQubeScanner"
                SONARQUBE_SERVER = "SonarQubeServer"
            }
            steps{
                echo '开始代码审查'
                script {
                    def scannerHome = tool "${SONARQUBE_SCANNER}"
                    withSonarQubeEnv("${SONARQUBE_SERVER}") {
                        sh "${scannerHome}/bin/sonar-scanner"
                    }
                }
                echo '代码审查完成'
            }
        }

        stage('构建镜像') {
            environment {
                // harbor仓库信息
HARBOR_URL = "harbor.local.com"
                HARBOR_PROJECT = "spring_boot_demo"
                // 镜像名称
                IMAGE_TAG = sh(script: "git rev-parse --short HEAD", returnStdout: true).trim()
            }
            steps {
                echo '开始构建镜像'
                script {
                    IMAGE_NAME = "${HARBOR_URL}/${HARBOR_PROJECT}/${IMAGE_APP}:${IMAGE_TAG}" 
                    docker.build "${IMAGE_NAME}"
                }
                echo '构建镜像完成'
                echo '开始推送镜像'
                script {
                    docker.withRegistry("https://${HARBOR_URL}", "${HARBOR_CRED}") {
                        docker.image("${IMAGE_NAME}").push()
                    }
                }
                echo '推送镜像完成'
                echo '开始删除镜像'
                script {
                    sh "docker rmi -f ${IMAGE_NAME}"
                }
                echo '删除镜像完成'
            }
        }

        stage('项目部署') {
            environment {
                // 目标主机信息
                HOST_NAME = "springboot1"
            }
            steps {
                echo '开始部署项目'
                // 获取harbor账号密码
                withCredentials([usernamePassword(credentialsId: "${HARBOR_CRED}", passwordVariable: 'HARBOR_PASSWORD', usernameVariable: 'HARBOR_USERNAME')]) {
                    // 执行远程命令
                    sshPublisher(publishers: [sshPublisherDesc(configName: "${HOST_NAME}", transfers: [sshTransfer(
                        cleanRemote: false, excludes: '', execCommand: "sh -x /opt/jenkins/springboot/deployment.sh ${HARBOR_USERNAME} ${HARBOR_PASSWORD} ${IMAGE_NAME} ${IMAGE_APP}", 
                        execTimeout: 120000, flatten: false, makeEmptyDirs: false, noDefaultExcludes: false, patternSeparator: '[, ]+', remoteDirectory: '/opt/jenkins/springboot', 
                        remoteDirectorySDF: false, removePrefix: '', sourceFiles: 'deployment.sh')], usePromotionTimestamp: false, useWorkspaceInPromotion: false, verbose: false
                    )])
                }
                echo '部署项目完成'
            }
        }
    }
    post {
        always {
            echo '开始发送邮件通知'
            // 构建后发送邮件
            emailext(
                subject: '构建通知:${PROJECT_NAME} - Build # ${BUILD_NUMBER} - ${BUILD_STATUS}!',
                body: '${FILE,path="email.html"}',
                to: 'cuiliang0302@qq.com'
            )
            echo '邮件通知发送完成'
        }
    }
}    

触发流水线后运行结果如下
image.png
邮件通知内容如下
image.png
至此,整个CICD流程完成。

查看更多

微信公众号

微信公众号同步更新,欢迎关注微信公众号《崔亮的博客》第一时间获取最近文章。

博客网站

崔亮的博客-专注devops自动化运维,传播优秀it运维技术文章。更多原创运维开发相关文章,欢迎访问https://www.cuiliangblog.cn

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

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

相关文章

求三个字符数组最大者(C语言)

一、N-S流程图&#xff1b; 二、运行结果&#xff1b; 三、源代码&#xff1b; # define _CRT_SECURE_NO_WARNINGS # include <stdio.h> # include <string.h>int main() {//初始化变量值&#xff1b;int i 0;char str[3][20];char string[20];//循环输入3个字符…

可以在手机端运行的大模型标杆:微软发布第三代Phi-3系列模型,评测结果超过同等参数规模水平,包含三个版本,最小38亿,最高140亿参数

本文原文来自DataLearnerAI官方网站&#xff1a; 可以在手机端运行的大模型标杆&#xff1a;微软发布第三代Phi-3系列模型&#xff0c;评测结果超过同等参数规模水平&#xff0c;包含三个版本&#xff0c;最小38亿&#xff0c;最高140亿参数 | 数据学习者官方网站(Datalearner…

React真的好难用

我发现React就像个宗教一样&#xff0c;网络上总有一群信徒。信徒&#xff1a;React天下第一&#xff0c;谁也不能说他不好。 网络上大佬对React的评价一般有几类&#xff1a; React跟Vue比就是手动档和自动档的区别&#xff0c;高手都开手动档。—— 就一个破打工的&#xf…

(待更)DRF: 序列化器、View、APIView、GenericAPIView、Mixin、ViewSet、ModelViewSet的源码解析

前言&#xff1a;还没有整理&#xff0c;后续有时间再整理&#xff0c;目前只是个人思路&#xff0c;文章较乱。 注意路径匹配的“/” 我们的url里面加了“/”&#xff0c;但是用apifox等非浏览器的工具发起请求时没有加“/”&#xff0c;而且还不是get请求&#xff0c;那么这…

【知识】pycolmap.Sift.extract的参数和返回格式

转载请注明出处&#xff1a;小锋学长生活大爆炸[xfxuezhagn.cn] 如果本文帮助到了你&#xff0c;请不吝给个[点赞、收藏、关注]哦~ import pycolmap# 比较推荐的参数 options {"peak_threshold": 0.0066667,"edge_threshold": 10,"first_octave"…

基于Spring Boot的火车订票管理系统设计与实现

基于Spring Boot的火车订票管理系统设计与实现 开发语言&#xff1a;Java框架&#xff1a;springbootJDK版本&#xff1a;JDK1.8数据库工具&#xff1a;Navicat11开发软件&#xff1a;eclipse/myeclipse/idea 系统部分展示 前台首页功能界面图&#xff0c;在系统首页可以查看…

python 函数作业 计算三角形的有效值s

题目&#xff1a; 计算半周长p&#xff0c;既p(abc)/2。 将p的值代入公式&#xff1a;计算出三角形的有效值。&#xff08;只能用python函数的方法求解&#xff09; 第一步先定义个函数&#xff1a; def isValid(side1,side2,side3) 第二步用if判断是否符合三角形边长准…

芯片尺寸封装(CSP)/晶圆级封装(WLP)/芯片尺寸晶圆级封装(CSWLP)

芯片尺寸封装&#xff08;CSP&#xff09;、晶圆级封装&#xff08;WLP&#xff09;、晶圆级芯片尺寸封装&#xff08;WLCSP&#xff09; 1.芯片尺寸封装&#xff08;CSP&#xff09;的定义是其尺寸不超过裸片尺寸的1.1倍。 2.晶圆级封装&#xff08;WLP&#xff09;是在晶圆…

AIGC——什么是人工智能生成内容

人工智能生成内容&#xff08;AIGC&#xff09;是当今数字时代的一个引人注目的前沿技术&#xff0c;它借助深度学习和自然语言处理等技术&#xff0c;使计算机系统具备了生成高质量文本、图像、音频等多媒体内容的能力。AIGC的出现不仅推动了信息技术的发展&#xff0c;也在多…

Linux搭建本地DNS服务器

目录 DNS进行域名解析的过程&#xff1a; 环境介绍&#xff1a; 环境准备&#xff1a; 1.安装bind 2.编辑主配 3.配置正向解析文件 4.测试&#xff1a; DNS进行域名解析的过程&#xff1a; 用户要访问www.baidu.com&#xff0c;会先找本机的host文件&#xff0c;再找本…

selenium 自动化测试课上实操指南2——乐视tv搜索

如果完成了实操1的同学&#xff0c;环境搭建已经ok&#xff0c;环境还没有好的同学请参考 实操1_百度搜索 为了大家顺利&#xff0c;我们还想按照实操1那样&#xff0c;先导入一个基本项目。在次基础上进行代码编写、 我们一起写写看。 1.打开乐视视频网页并最大化 如下图所…

深度学习从入门到精通——词向量介绍及应用

词向量介绍 词向量&#xff08;Word embedding&#xff09;&#xff0c;即把词语表示成实数向量。“好”的词向量能体现词语直接的相近关系。词向量已经被证明可以提高NLP任务的性能&#xff0c;例如语法分析和情感分析。词向量与词嵌入技术的提出是为了解决onehot的缺陷。它把…

【Yolov系列】Yolov5学习(一):大致框架

一、Yolov5网络结构 Yolov5特点&#xff1a; 合适于移动端部署&#xff0c;模型小&#xff0c;速度快 Yolov5骨干结构&#xff1a;CSPDarknet53网络Yolov5主要有Yolov5s、Yolov5m、Yolov5l、Yolov5x四个版本。这几个模型的结构基本一样&#xff0c;不同的是depth_multiple模型…

AUTOSAR-SD篇

1 概述 服务发现模块的主要任务是管理在车内通信中被称为服务的功能实体的可用性&#xff0c;以及控制事件消息的发送行为。只允许向需要这些事件消息的接收器发送事件消息&#xff08;发布/订阅&#xff09;。 这里描述的解决方案也被称为SOME/IP-SD&#xff08;基于IP -服务发…

西湖大学赵世钰老师【强化学习的数学原理】学习笔记-1、0节

强化学习的数学原理是由西湖大学赵世钰老师带来的关于RL理论方面的详细课程&#xff0c;本课程深入浅出地介绍了RL的基础原理&#xff0c;前置技能只需要基础的编程能力、概率论以及一部分的高等数学&#xff0c;你听完之后会在大脑里面清晰的勾勒出RL公式推导链条中的每一个部…

索引失效的几种场景

索引失效的几种场景 初始化数据一、对索引使用左或左右模糊匹配二、对索引使用函数三、对索引使用表达式计算四、对索引进行隐式类型转换五、索引使用不满足最左前缀原则六、where子句使用or总结 初始化数据 本文使用的是InnoDB存储引擎&#xff0c;先来创建一个学生表。 dro…

2021年山东省职业院校技能大赛高职组“信息安全管理与评估”样题

培训、环境、资料、考证 公众号&#xff1a;Geek极安云科 网络安全群&#xff1a;624032112 网络系统管理群&#xff1a;223627079 网络建设与运维群&#xff1a;870959784 移动应用开发群&#xff1a;548238632 极安云科专注于技能提升&#xff0c;赋能 2024年广东省高校的技…

【第二十五课】动态规划:数字三角形(acwing-898 / 蓝桥官网503 / c++代码)

目录 acwing-898数字三角形(模板题) 思路 注意点 代码 视频讲解推荐 2020蓝桥杯省赛-数字三角形 错误思路 (可不看) 思路 代码 注意点 续上之前的啦。 【第二十五课】动态规划&#xff1a;01背包问题(acwing-2 / 思路 / 含一维数组优化 / c代码) 适合在学习过背包…

OS复习笔记ch3-1

引言 学到第三章&#xff0c;就正式步入我们OS的大门了 本章我们将围绕以下几个问题去解决 什么是进程&#xff1f;进程状态有哪些&#xff1f;进程如何描述&#xff1f;进程如何控制&#xff1f; 本节内容主要是回答前两个问题&#xff0c;第二节回答后两个问题。 进程 …

windows环境下安装Apache

首先apache官网下载地址&#xff1a;http://www.apachelounge.com/download/按照自己的电脑操作系统来安装 这里我安装的是win64 主版本是2.4的apache。 然后解压压缩包到一个全英文的路径下&#xff01;&#xff01;&#xff01;一定一定不要有中文 中文符号也不要有&#xff…